001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.command; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.Arrays; 007import java.util.Collection; 008import java.util.HashSet; 009 010import javax.swing.Icon; 011 012import org.openstreetmap.josm.data.osm.OsmPrimitive; 013import org.openstreetmap.josm.tools.ImageProvider; 014import org.openstreetmap.josm.tools.Utils; 015 016/** 017 * A command consisting of a sequence of other commands. Executes the other commands 018 * and undo them in reverse order. 019 * @author imi 020 * @since 31 021 */ 022public class SequenceCommand extends Command { 023 024 /** The command sequence to be executed. */ 025 private Command[] sequence; 026 private boolean sequenceComplete; 027 private final String name; 028 /** Determines if the sequence execution should continue after one of its commands fails. */ 029 public boolean continueOnError = false; 030 031 /** 032 * Create the command by specifying the list of commands to execute. 033 * @param name The description text 034 * @param sequenz The sequence that should be executed. 035 */ 036 public SequenceCommand(String name, Collection<Command> sequenz) { 037 super(); 038 this.name = name; 039 this.sequence = sequenz.toArray(new Command[sequenz.size()]); 040 } 041 042 /** 043 * Convenient constructor, if the commands are known at compile time. 044 * @param name The description text 045 * @param sequenz The sequence that should be executed. 046 */ 047 public SequenceCommand(String name, Command... sequenz) { 048 this(name, Arrays.asList(sequenz)); 049 } 050 051 @Override public boolean executeCommand() { 052 for (int i=0; i < sequence.length; i++) { 053 boolean result = sequence[i].executeCommand(); 054 if (!result && !continueOnError) { 055 undoCommands(i-1); 056 return false; 057 } 058 } 059 sequenceComplete = true; 060 return true; 061 } 062 063 /** 064 * Returns the last command. 065 * @return The last command, or {@code null} if the sequence is empty. 066 */ 067 public Command getLastCommand() { 068 if (sequence.length == 0) 069 return null; 070 return sequence[sequence.length-1]; 071 } 072 073 protected final void undoCommands(int start) { 074 // We probably aborted this halfway though the 075 // execution sequence because of a sub-command 076 // error. We already undid the sub-commands. 077 if (!sequenceComplete) 078 return; 079 for (int i = start; i >= 0; --i) { 080 sequence[i].undoCommand(); 081 } 082 } 083 084 @Override public void undoCommand() { 085 undoCommands(sequence.length-1); 086 } 087 088 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 089 for (Command c : sequence) { 090 c.fillModifiedData(modified, deleted, added); 091 } 092 } 093 094 @Override 095 public String getDescriptionText() { 096 return tr("Sequence: {0}", name); 097 } 098 099 @Override 100 public Icon getDescriptionIcon() { 101 return ImageProvider.get("data", "sequence"); 102 } 103 104 @Override 105 public Collection<PseudoCommand> getChildren() { 106 return Arrays.<PseudoCommand>asList(sequence); 107 } 108 109 @Override 110 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 111 Collection<OsmPrimitive> prims = new HashSet<>(); 112 for (Command c : sequence) { 113 prims.addAll(c.getParticipatingPrimitives()); 114 } 115 return prims; 116 } 117 118 protected final void setSequence(Command[] sequence) { 119 this.sequence = Utils.copyArray(sequence); 120 } 121 122 protected final void setSequenceComplete(boolean sequenceComplete) { 123 this.sequenceComplete = sequenceComplete; 124 } 125}