001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj2.command;
006
007import java.util.Collection;
008import java.util.Collections;
009import java.util.Set;
010import java.util.WeakHashMap;
011
012/**
013 * A base for CommandGroups. Statically tracks commands that have been allocated to groups to ensure
014 * those commands are not also used independently, which can result in inconsistent command state
015 * and unpredictable execution.
016 */
017public abstract class CommandGroupBase extends CommandBase {
018  private static final Set<Command> m_groupedCommands =
019      Collections.newSetFromMap(new WeakHashMap<>());
020
021  static void registerGroupedCommands(Command... commands) {
022    m_groupedCommands.addAll(Set.of(commands));
023  }
024
025  /**
026   * Clears the list of grouped commands, allowing all commands to be freely used again.
027   *
028   * <p>WARNING: Using this haphazardly can result in unexpected/undesirable behavior. Do not use
029   * this unless you fully understand what you are doing.
030   */
031  public static void clearGroupedCommands() {
032    m_groupedCommands.clear();
033  }
034
035  /**
036   * Removes a single command from the list of grouped commands, allowing it to be freely used
037   * again.
038   *
039   * <p>WARNING: Using this haphazardly can result in unexpected/undesirable behavior. Do not use
040   * this unless you fully understand what you are doing.
041   *
042   * @param command the command to remove from the list of grouped commands
043   */
044  public static void clearGroupedCommand(Command command) {
045    m_groupedCommands.remove(command);
046  }
047
048  /**
049   * Requires that the specified commands not have been already allocated to a CommandGroup. Throws
050   * an {@link IllegalArgumentException} if commands have been allocated.
051   *
052   * @param commands The commands to check
053   */
054  public static void requireUngrouped(Command... commands) {
055    requireUngrouped(Set.of(commands));
056  }
057
058  /**
059   * Requires that the specified commands not have been already allocated to a CommandGroup. Throws
060   * an {@link IllegalArgumentException} if commands have been allocated.
061   *
062   * @param commands The commands to check
063   */
064  public static void requireUngrouped(Collection<Command> commands) {
065    if (!Collections.disjoint(commands, getGroupedCommands())) {
066      throw new IllegalArgumentException("Commands cannot be added to more than one CommandGroup");
067    }
068  }
069
070  static Set<Command> getGroupedCommands() {
071    return m_groupedCommands;
072  }
073
074  /**
075   * Adds the given commands to the command group.
076   *
077   * @param commands The commands to add.
078   */
079  public abstract void addCommands(Command... commands);
080
081  /**
082   * Factory method for {@link SequentialCommandGroup}, included for brevity/convenience.
083   *
084   * @param commands the commands to include
085   * @return the command group
086   */
087  public static CommandGroupBase sequence(Command... commands) {
088    return new SequentialCommandGroup(commands);
089  }
090
091  /**
092   * Factory method for {@link ParallelCommandGroup}, included for brevity/convenience.
093   *
094   * @param commands the commands to include
095   * @return the command group
096   */
097  public static CommandGroupBase parallel(Command... commands) {
098    return new ParallelCommandGroup(commands);
099  }
100
101  /**
102   * Factory method for {@link ParallelRaceGroup}, included for brevity/convenience.
103   *
104   * @param commands the commands to include
105   * @return the command group
106   */
107  public static CommandGroupBase race(Command... commands) {
108    return new ParallelRaceGroup(commands);
109  }
110
111  /**
112   * Factory method for {@link ParallelDeadlineGroup}, included for brevity/convenience.
113   *
114   * @param deadline the deadline command
115   * @param commands the commands to include
116   * @return the command group
117   */
118  public static CommandGroupBase deadline(Command deadline, Command... commands) {
119    return new ParallelDeadlineGroup(deadline, commands);
120  }
121}