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.function.Supplier;
011
012/**
013 * Schedules a given command when this command is initialized and ends when it ends, but does not
014 * directly run it. Use this for including a command in a composition without adding its
015 * requirements, <strong>but only if you know what you are doing. If you are unsure, see <a
016 * href="https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands">the
017 * WPILib docs</a> for a complete explanation of proxy semantics.</strong> Do not proxy a command
018 * from a subsystem already required by the composition, or else the composition will cancel itself
019 * when the proxy is reached. If this command is interrupted, it will cancel the command.
020 *
021 * <p>This class is provided by the NewCommands VendorDep
022 */
023public class ProxyCommand extends Command {
024  private final Supplier<Command> m_supplier;
025  private Command m_command;
026
027  /**
028   * Creates a new ProxyCommand that schedules the supplied command when initialized, and ends when
029   * it is no longer scheduled. Use this for lazily creating <strong>proxied</strong> commands at
030   * runtime. Proxying should only be done to escape from composition requirement semantics, so if
031   * only initialization time command construction is needed, use {@link DeferredCommand} instead.
032   *
033   * @param supplier the command supplier
034   * @deprecated This constructor's similarity to {@link DeferredCommand} is confusing and opens
035   *     potential footguns for users who do not fully understand the semantics and implications of
036   *     proxying, but who simply want runtime construction. Users who do know what they are doing
037   *     and need a supplier-constructed proxied command should instead defer a proxy command.
038   * @see DeferredCommand
039   */
040  @Deprecated(since = "2025", forRemoval = true)
041  public ProxyCommand(Supplier<Command> supplier) {
042    m_supplier = requireNonNullParam(supplier, "supplier", "ProxyCommand");
043  }
044
045  /**
046   * Creates a new ProxyCommand that schedules the given command when initialized, and ends when it
047   * is no longer scheduled.
048   *
049   * @param command the command to run by proxy
050   */
051  @SuppressWarnings("this-escape")
052  public ProxyCommand(Command command) {
053    Command nullCheckedCommand = requireNonNullParam(command, "command", "ProxyCommand");
054    m_supplier = () -> nullCheckedCommand;
055    setName("Proxy(" + nullCheckedCommand.getName() + ")");
056  }
057
058  @Override
059  public void initialize() {
060    m_command = m_supplier.get();
061    m_command.schedule();
062  }
063
064  @Override
065  public void end(boolean interrupted) {
066    if (interrupted) {
067      m_command.cancel();
068    }
069    m_command = null;
070  }
071
072  @Override
073  public void execute() {}
074
075  @Override
076  public boolean isFinished() {
077    // because we're between `initialize` and `end`, `m_command` is necessarily not null
078    // but if called otherwise and m_command is null,
079    // it's UB, so we can do whatever we want -- like return true.
080    return m_command == null || !m_command.isScheduled();
081  }
082
083  /**
084   * Whether the given command should run when the robot is disabled. Override to return true if the
085   * command should run when disabled.
086   *
087   * @return true. Otherwise, this proxy would cancel commands that do run when disabled.
088   */
089  @Override
090  public boolean runsWhenDisabled() {
091    return true;
092  }
093
094  @Override
095  public void initSendable(SendableBuilder builder) {
096    super.initSendable(builder);
097    builder.addStringProperty(
098        "proxied", () -> m_command == null ? "null" : m_command.getName(), null);
099  }
100}