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 static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
008
009import edu.wpi.first.util.sendable.SendableBuilder;
010import java.util.Set;
011import java.util.function.Supplier;
012
013/**
014 * Defers Command construction to runtime. Runs the command returned by the supplier when this
015 * command is initialized, and ends when it ends. Useful for performing runtime tasks before
016 * creating a new command. If this command is interrupted, it will cancel the command.
017 *
018 * <p>Note that the supplier <i>must</i> create a new Command each call. For selecting one of a
019 * preallocated set of commands, use {@link SelectCommand}.
020 *
021 * <p>This class is provided by the NewCommands VendorDep
022 */
023public class DeferredCommand extends Command {
024  private final Command m_nullCommand =
025      new PrintCommand("[DeferredCommand] Supplied command was null!");
026
027  private final Supplier<Command> m_supplier;
028  private Command m_command = m_nullCommand;
029
030  /**
031   * Creates a new DeferredCommand that runs the supplied command when initialized, and ends when it
032   * ends. Useful for lazily creating commands at runtime. The {@link Supplier} will be called each
033   * time this command is initialized. The Supplier <i>must</i> create a new Command each call.
034   *
035   * @param supplier The command supplier
036   * @param requirements The command requirements. This is a {@link Set} to prevent accidental
037   *     omission of command requirements. Use {@link Set#of()} to easily construct a requirement
038   *     set.
039   */
040  @SuppressWarnings("this-escape")
041  public DeferredCommand(Supplier<Command> supplier, Set<Subsystem> requirements) {
042    m_supplier = requireNonNullParam(supplier, "supplier", "DeferredCommand");
043    addRequirements(requirements.toArray(new Subsystem[0]));
044  }
045
046  @Override
047  public void initialize() {
048    var cmd = m_supplier.get();
049    if (cmd != null) {
050      m_command = cmd;
051      CommandScheduler.getInstance().registerComposedCommands(m_command);
052    }
053    m_command.initialize();
054  }
055
056  @Override
057  public void execute() {
058    m_command.execute();
059  }
060
061  @Override
062  public boolean isFinished() {
063    return m_command.isFinished();
064  }
065
066  @Override
067  public void end(boolean interrupted) {
068    m_command.end(interrupted);
069    m_command = m_nullCommand;
070  }
071
072  @Override
073  @SuppressWarnings("PMD.CompareObjectsWithEquals")
074  public void initSendable(SendableBuilder builder) {
075    super.initSendable(builder);
076    builder.addStringProperty(
077        "deferred", () -> m_command == m_nullCommand ? "null" : m_command.getName(), null);
078  }
079}