KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > emf > common > command > CompoundCommand


1 /**
2  * <copyright>
3  *
4  * Copyright (c) 2002-2004 IBM Corporation and others.
5  * All rights reserved. This program and the accompanying materials
6  * are made available under the terms of the Eclipse Public License v1.0
7  * which accompanies this distribution, and is available at
8  * http://www.eclipse.org/legal/epl-v10.html
9  *
10  * Contributors:
11  * IBM - Initial API and implementation
12  *
13  * </copyright>
14  *
15  * $Id: CompoundCommand.java,v 1.3 2005/06/08 05:44:08 nickb Exp $
16  */

17 package org.eclipse.emf.common.command;
18
19
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.ListIterator JavaDoc;
26
27 import org.eclipse.emf.common.CommonPlugin;
28 import org.eclipse.emf.common.util.WrappedException;
29
30
31 /**
32  * A command that comprises a sequence of subcommands.
33  * Derived classes can control the way results are accumulated from the individual commands;
34  * the default behaviour is to return the result of the last command.
35  */

36 public class CompoundCommand extends AbstractCommand
37 {
38   /**
39    * The list of subcommands.
40    */

41   protected List JavaDoc commandList;
42
43   /**
44    * When {@link #resultIndex} is set to this,
45    * {@link #getResult} and {@link #getAffectedObjects} are delegated to the last command, if any, in the list.
46    */

47   public static final int LAST_COMMAND_ALL = Integer.MIN_VALUE;
48
49   /**
50    * When {@link #resultIndex} is set to this,
51    * {@link #getResult} and {@link #getAffectedObjects}
52    * are set to the result of merging the corresponding collection of each command in the list.
53    */

54   public static final int MERGE_COMMAND_ALL = Integer.MIN_VALUE - 1;
55
56   /**
57    * The index of the command whose result and affected objects are forwarded.
58    * Negative values have special meaning, as defined by the static constants.
59    * A value of -1 indicates that the last command in the list should be used.
60    * We could have more special behaviours implemented for other negative values.
61    */

62   protected int resultIndex = MERGE_COMMAND_ALL;
63
64   /**
65    * Creates an empty instance.
66    */

67   public CompoundCommand()
68   {
69     super();
70     commandList = new ArrayList JavaDoc();
71   }
72
73   /**
74    * Creates an instance with the given label.
75    * @param label the label.
76    */

77   public CompoundCommand(String JavaDoc label)
78   {
79     super(label);
80     commandList = new ArrayList JavaDoc();
81   }
82   
83   /**
84    * Creates an instance with the given label and description.
85    * @param label the label.
86    * @param description the description.
87    */

88   public CompoundCommand(String JavaDoc label, String JavaDoc description)
89   {
90     super(label, description);
91     commandList = new ArrayList JavaDoc();
92   }
93   
94   /**
95    * Creates an instance with the given list.
96    * @param commandList the list of commands.
97    */

98   public CompoundCommand(List JavaDoc commandList)
99   {
100     super();
101     this.commandList = commandList;
102   }
103
104   /**
105    * Creates instance with the given label and list.
106    * @param label the label.
107    * @param commandList the list of commands.
108    */

109   public CompoundCommand(String JavaDoc label, List JavaDoc commandList)
110   {
111     super(label);
112     this.commandList = commandList;
113   }
114
115   /**
116    * Creates an instance with the given label, description, and list.
117    * @param label the label.
118    * @param description the description.
119    * @param commandList the list of commands.
120    */

121   public CompoundCommand(String JavaDoc label, String JavaDoc description, List JavaDoc commandList)
122   {
123     super(label, description);
124     this.commandList = commandList;
125   }
126
127   /**
128    * Creates an empty instance with the given result index.
129    * @param resultIndex the {@link #resultIndex}.
130    */

131   public CompoundCommand(int resultIndex)
132   {
133     super();
134     this.resultIndex = resultIndex;
135     commandList = new ArrayList JavaDoc();
136   }
137
138   /**
139    * Creates an instance with the given result index and label.
140    * @param resultIndex the {@link #resultIndex}.
141    * @param label the label.
142    */

143   public CompoundCommand(int resultIndex, String JavaDoc label)
144   {
145     super(label);
146     this.resultIndex = resultIndex;
147     commandList = new ArrayList JavaDoc();
148   }
149   
150   /**
151    * Creates an instance with the given result index, label, and description.
152    * @param resultIndex the {@link #resultIndex}.
153    * @param label the label.
154    * @param description the description.
155    */

156   public CompoundCommand(int resultIndex, String JavaDoc label, String JavaDoc description)
157   {
158     super(label, description);
159     this.resultIndex = resultIndex;
160     commandList = new ArrayList JavaDoc();
161   }
162   
163   /**
164    * Creates an instance with the given result index and list.
165    * @param resultIndex the {@link #resultIndex}.
166    * @param commandList the list of commands.
167    */

168   public CompoundCommand(int resultIndex, List JavaDoc commandList)
169   {
170     super();
171     this.resultIndex = resultIndex;
172     this.commandList = commandList;
173   }
174
175   /**
176    * Creates an instance with the given resultIndex, label, and list.
177    * @param resultIndex the {@link #resultIndex}.
178    * @param label the label.
179    * @param commandList the list of commands.
180    */

181   public CompoundCommand(int resultIndex, String JavaDoc label, List JavaDoc commandList)
182   {
183     super(label);
184     this.resultIndex = resultIndex;
185     this.commandList = commandList;
186   }
187
188   /**
189    * Creates an instance with the given result index, label, description, and list.
190    * @param resultIndex the {@link #resultIndex}.
191    * @param label the label.
192    * @param description the description.
193    * @param commandList the list of commands.
194    */

195   public CompoundCommand(int resultIndex, String JavaDoc label, String JavaDoc description, List JavaDoc commandList)
196   {
197     super(label, description);
198     this.resultIndex = resultIndex;
199     this.commandList = commandList;
200   }
201
202   /**
203    * Returns whether there are commands in the list.
204    * @return whether there are commands in the list.
205    */

206   public boolean isEmpty()
207   {
208     return commandList.isEmpty();
209   }
210
211   /**
212    * Returns an unmodifiable view of the commands in the list.
213    * @return an unmodifiable view of the commands in the list.
214    */

215   public List JavaDoc getCommandList()
216   {
217     return Collections.unmodifiableList(commandList);
218   }
219
220   /**
221    * Returns the index of the command whose result and affected objects are forwarded.
222    * Negative values have special meaning, as defined by the static constants.
223    * @return the index of the command whose result and affected objects are forwarded.
224    * @see #LAST_COMMAND_ALL
225    * @see #MERGE_COMMAND_ALL
226    */

227   public int getResultIndex()
228   {
229     return resultIndex;
230   }
231
232   /**
233    * Returns whether all the commands can execute so that {@link #isExecutable} can be cached.
234    * An empty command list causes <code>false</code> to be returned.
235    * @return whether all the commands can execute.
236    */

237   protected boolean prepare()
238   {
239     if (commandList.isEmpty())
240     {
241       return false;
242     }
243     else
244     {
245       for (Iterator JavaDoc commands = commandList.listIterator(); commands.hasNext(); )
246       {
247         Command command = (Command)commands.next();
248         if (!command.canExecute())
249         {
250           return false;
251         }
252       }
253
254       return true;
255     }
256   }
257
258   /**
259    * Calls {@link Command#execute} for each command in the list.
260    */

261   public void execute()
262   {
263     for (ListIterator JavaDoc commands = commandList.listIterator(); commands.hasNext(); )
264     {
265       try
266       {
267         Command command = (Command)commands.next();
268         command.execute();
269       }
270       catch (RuntimeException JavaDoc exception)
271       {
272         // Skip over the command that threw the exception.
273
//
274
commands.previous();
275
276         try
277         {
278           // Iterate back over the executed commands to undo them.
279
//
280
while (commands.hasPrevious())
281           {
282             Command command = (Command)commands.previous();
283             if (command.canUndo())
284             {
285               command.undo();
286             }
287             else
288             {
289               break;
290             }
291           }
292         }
293         catch (RuntimeException JavaDoc nestedException)
294         {
295           CommonPlugin.INSTANCE.log
296             (new WrappedException
297               (CommonPlugin.INSTANCE.getString("_UI_IgnoreException_exception"), nestedException).fillInStackTrace());
298         }
299
300         throw exception;
301       }
302     }
303   }
304
305   /**
306    * Returns <code>false</code> if any of the commands return <code>false</code> for {@link Command#canUndo}.
307    * @return <code>false</code> if any of the commands return <code>false</code> for <code>canUndo</code>.
308    */

309   public boolean canUndo()
310   {
311     for (Iterator JavaDoc commands = commandList.listIterator(); commands.hasNext(); )
312     {
313       Command command = (Command)commands.next();
314       if (!command.canUndo())
315       {
316         return false;
317       }
318     }
319
320     return true;
321   }
322
323   /**
324    * Calls {@link Command#undo} for each command in the list, in reverse order.
325    */

326   public void undo()
327   {
328     for (ListIterator JavaDoc commands = commandList.listIterator(commandList.size()); commands.hasPrevious(); )
329     {
330       try
331       {
332         Command command = (Command)commands.previous();
333         command.undo();
334       }
335       catch (RuntimeException JavaDoc exception)
336       {
337         // Skip over the command that threw the exception.
338
//
339
commands.next();
340
341         try
342         {
343           // Iterate forward over the undone commands to redo them.
344
//
345
while (commands.hasNext())
346           {
347             Command command = (Command)commands.next();
348             command.redo();
349           }
350         }
351         catch (RuntimeException JavaDoc nestedException)
352         {
353           CommonPlugin.INSTANCE.log
354             (new WrappedException
355               (CommonPlugin.INSTANCE.getString("_UI_IgnoreException_exception"), nestedException).fillInStackTrace());
356         }
357
358
359         throw exception;
360       }
361     }
362   }
363
364   /**
365    * Calls {@link Command#redo} for each command in the list.
366    */

367   public void redo()
368   {
369     for (ListIterator JavaDoc commands = commandList.listIterator(); commands.hasNext(); )
370     {
371       try
372       {
373         Command command = (Command)commands.next();
374         command.redo();
375       }
376       catch (RuntimeException JavaDoc exception)
377       {
378         // Skip over the command that threw the exception.
379
//
380
commands.previous();
381
382         try
383         {
384           // Iterate back over the executed commands to undo them.
385
//
386
while (commands.hasPrevious())
387           {
388             Command command = (Command)commands.previous();
389             command.undo();
390           }
391         }
392         catch (RuntimeException JavaDoc nestedException)
393         {
394           CommonPlugin.INSTANCE.log
395             (new WrappedException
396               (CommonPlugin.INSTANCE.getString("_UI_IgnoreException_exception"), nestedException).fillInStackTrace());
397         }
398
399         throw exception;
400       }
401     }
402   }
403
404   /**
405    * Determines the result by composing the results of the commands in the list;
406    * this is affected by the setting of {@link #resultIndex}.
407    * @return the result.
408    */

409   public Collection JavaDoc getResult()
410   {
411     if (commandList.isEmpty())
412     {
413       return Collections.EMPTY_LIST;
414     }
415     else if (resultIndex == LAST_COMMAND_ALL)
416     {
417       return ((Command)commandList.get(commandList.size() - 1)).getResult();
418     }
419     else if (resultIndex == MERGE_COMMAND_ALL)
420     {
421       return getMergedResultCollection();
422     }
423     else if (resultIndex < commandList.size())
424     {
425       return ((Command)commandList.get(resultIndex)).getResult();
426     }
427     else
428     {
429       return Collections.EMPTY_LIST;
430     }
431   }
432
433   /**
434    * Returns the merged collection of all command results.
435    * @return the merged collection of all command results.
436    */

437   protected Collection JavaDoc getMergedResultCollection()
438   {
439     Collection JavaDoc result = new ArrayList JavaDoc();
440
441     for (Iterator JavaDoc commands = commandList.iterator(); commands.hasNext(); )
442     {
443       Command command = (Command)commands.next();
444       result.addAll(command.getResult());
445     }
446
447     return result;
448   }
449
450
451   /**
452    * Determines the affected objects by composing the affected objects of the commands in the list;
453    * this is affected by the setting of {@link #resultIndex}.
454    * @return the affected objects.
455    */

456   public Collection JavaDoc getAffectedObjects()
457   {
458     if (commandList.isEmpty())
459     {
460       return Collections.EMPTY_LIST;
461     }
462     else if (resultIndex == LAST_COMMAND_ALL)
463     {
464       return ((Command)commandList.get(commandList.size() - 1)).getAffectedObjects();
465     }
466     else if (resultIndex == MERGE_COMMAND_ALL)
467     {
468       return getMergedAffectedObjectsCollection();
469     }
470     else if (resultIndex < commandList.size())
471     {
472       return ((Command)commandList.get(resultIndex)).getAffectedObjects();
473     }
474     else
475     {
476       return Collections.EMPTY_LIST;
477     }
478   }
479
480   /**
481    * Returns the merged collection of all command affected objects.
482    * @return the merged collection of all command affected objects.
483    */

484   protected Collection JavaDoc getMergedAffectedObjectsCollection()
485   {
486     Collection JavaDoc result = new ArrayList JavaDoc();
487
488     for (Iterator JavaDoc commands = commandList.iterator(); commands.hasNext(); )
489     {
490       Command command = (Command)commands.next();
491       result.addAll(command.getAffectedObjects());
492     }
493
494     return result;
495   }
496
497   /**
498    * Determines the label by composing the labels of the commands in the list;
499    * this is affected by the setting of {@link #resultIndex}.
500    * @return the label.
501    */

502   public String JavaDoc getLabel()
503   {
504     if (label != null)
505     {
506       return label;
507     }
508     else if (commandList.isEmpty())
509     {
510       return CommonPlugin.INSTANCE.getString("_UI_CompoundCommand_label");
511     }
512     else if (resultIndex == LAST_COMMAND_ALL || resultIndex == MERGE_COMMAND_ALL)
513     {
514       return ((Command)commandList.get(commandList.size() - 1)).getLabel();
515     }
516     else if (resultIndex < commandList.size())
517     {
518       return ((Command)commandList.get(resultIndex)).getLabel();
519     }
520     else
521     {
522       return CommonPlugin.INSTANCE.getString("_UI_CompoundCommand_label");
523     }
524   }
525
526   /**
527    * Determines the description by composing the descriptions of the commands in the list;
528    * this is affected by the setting of {@link #resultIndex}.
529    * @return the description.
530    */

531   public String JavaDoc getDescription()
532   {
533     if (description != null)
534     {
535       return description;
536     }
537     else if (commandList.isEmpty())
538     {
539       return CommonPlugin.INSTANCE.getString("_UI_CompoundCommand_description");
540     }
541     else if (resultIndex == LAST_COMMAND_ALL || resultIndex == MERGE_COMMAND_ALL)
542     {
543       return ((Command)commandList.get(commandList.size() - 1)).getDescription();
544     }
545     else if (resultIndex < commandList.size())
546     {
547       return ((Command)commandList.get(resultIndex)).getDescription();
548     }
549     else
550     {
551       return CommonPlugin.INSTANCE.getString("_UI_CompoundCommand_description");
552     }
553   }
554
555   /**
556    * Adds a command to this compound command's list of commands.
557    * @param command the command to append.
558    */

559   public void append(Command command)
560   {
561     if (command != null)
562     {
563       commandList.add(command);
564     }
565   }
566
567   /**
568    * Checks if the command can execute;
569    * if so, it is executed, appended to the list, and true is returned,
570    * if not, it is just disposed and false is returned.
571    * A typical use for this is to execute commands created during the execution of another command, e.g.,
572    * <pre>
573    * class MyCommand extends CommandBase
574    * {
575    * protected Command subcommand;
576    *
577    * //...
578    *
579    * public void execute()
580    * {
581    * // ...
582    * Compound subcommands = new CompoundCommand();
583    * subcommands.appendAndExecute(new AddCommand(...));
584    * if (condition) subcommands.appendAndExecute(new AddCommand(...));
585    * subcommand = subcommands.unwrap();
586    * }
587    *
588    * public void undo()
589    * {
590    * // ...
591    * subcommand.undo();
592    * }
593    *
594    * public void redo()
595    * {
596    * // ...
597    * subcommand.redo();
598    * }
599    *
600    * public void dispose()
601    * {
602    * // ...
603    * if (subcommand != null)
604    * {
605    * subcommand.dispose();
606    * }
607    * }
608    * }
609    * </pre>
610    * Another use is in an execute override of compound command itself:
611    * <pre>
612    * class MyCommand extends CompoundCommand
613    * {
614    * public void execute()
615    * {
616    * // ...
617    * appendAndExecute(new AddCommand(...));
618    * if (condition) appendAndExecute(new AddCommand(...));
619    * }
620    * }
621    * </pre>
622    * Note that appending commands will modify what getResult and getAffectedObjects return,
623    * so you may want to set the resultIndex flag.
624    * @param command the command.
625    * @return whether the command was successfully executed and appended.
626    */

627   public boolean appendAndExecute(Command command)
628   {
629     if (command != null)
630     {
631       if (!isPrepared)
632       {
633         if (commandList.isEmpty())
634         {
635           isPrepared = true;
636           isExecutable = true;
637         }
638         else
639         {
640           isExecutable = prepare();
641           isPrepared = true;
642           if (isExecutable)
643           {
644             execute();
645           }
646         }
647       }
648
649       if (command.canExecute())
650       {
651         try
652         {
653           command.execute();
654           commandList.add(command);
655           return true;
656         }
657         catch (RuntimeException JavaDoc exception)
658         {
659           CommonPlugin.INSTANCE.log
660             (new WrappedException
661               (CommonPlugin.INSTANCE.getString("_UI_IgnoreException_exception"), exception).fillInStackTrace());
662         }
663       }
664
665       command.dispose();
666     }
667
668     return false;
669   }
670
671   /**
672    * Adds a command to this compound command's the list of commands and returns <code>true</code>,
673    * if <code>command.{@link org.eclipse.emf.common.command.Command#canExecute() canExecute()}</code> returns true;
674    * otherwise, it simply calls <code>command.{@link org.eclipse.emf.common.command.Command#dispose() dispose()}</code>
675    * and returns <code>false</code>.
676    * @param command the command.
677    * @return whether the command was executed and appended.
678    */

679   public boolean appendIfCanExecute(Command command)
680   {
681     if (command == null)
682     {
683       return false;
684     }
685     else if (command.canExecute())
686     {
687       commandList.add(command);
688       return true;
689     }
690     else
691     {
692       command.dispose();
693       return false;
694     }
695   }
696
697   /**
698    * Calls {@link Command#dispose} for each command in the list.
699    */

700   public void dispose()
701   {
702     for (Iterator JavaDoc commands = commandList.listIterator(); commands.hasNext(); )
703     {
704       Command command = (Command)commands.next();
705       command.dispose();
706     }
707   }
708
709   /**
710    * Returns one of three things:
711    * {@link org.eclipse.emf.common.command.UnexecutableCommand#INSTANCE}, if there are no commands,
712    * the one command, if there is exactly one command,
713    * or <code>this</code>, if there are multiple commands;
714    * this command is {@link #dispose}d in the first two cases.
715    * You should only unwrap a compound command if you created it for that purpose, e.g.,
716    * <pre>
717    * CompoundCommand subcommands = new CompoundCommand();
718    * subcommands.append(x);
719    * if (condition) subcommands.append(y);
720    * Command result = subcommands.unwrap();
721    * </pre>
722    * is a good way to create an efficient accumulated result.
723    * @return the unwapped command.
724    */

725   public Command unwrap()
726   {
727     switch (commandList.size())
728     {
729       case 0:
730       {
731         dispose();
732         return UnexecutableCommand.INSTANCE;
733       }
734       case 1:
735       {
736         Command result = (Command)commandList.remove(0);
737         dispose();
738         return result;
739       }
740       default:
741       {
742         return this;
743       }
744     }
745   }
746
747   /*
748    * Javadoc copied from base class.
749    */

750   public String JavaDoc toString()
751   {
752     StringBuffer JavaDoc result = new StringBuffer JavaDoc(super.toString());
753     result.append(" (commandList: #" + commandList.size() + ")");
754     result.append(" (resultIndex: " + resultIndex + ")");
755
756     return result.toString();
757   }
758 }
759
Popular Tags