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