ContractInterface.java

/*
 * @copyright defined in LICENSE.txt
 */

package hera.api.model;

import static hera.util.ValidationUtils.assertNotNull;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;

import hera.annotation.ApiAudience;
import hera.annotation.ApiStability;
import hera.exception.HerajException;
import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

@ApiAudience.Public
@ApiStability.Unstable
@ToString
@EqualsAndHashCode
public class ContractInterface {

  @Getter
  protected final ContractAddress address;

  @Getter
  protected final String version;

  @Getter
  protected final String language;

  @Getter
  protected final List<ContractFunction> functions;

  @Getter
  protected final List<StateVariable> stateVariables;

  /**
   * ContractInterface constructor.
   * 
   * @param contractAddress a contract address
   * @param version a contract version
   * @param language a contract language
   * @param functions a contract functions to invoke
   * @param stateVariables a contract state variables
   */
  @ApiAudience.Private
  public ContractInterface(final ContractAddress contractAddress, final String version,
      final String language, final List<ContractFunction> functions,
      final List<StateVariable> stateVariables) {
    assertNotNull(contractAddress, "Contract address must not null");
    assertNotNull(version, "Version must not null");
    assertNotNull(language, "Language must not null");
    assertNotNull(functions, "Functions must not null");
    assertNotNull(stateVariables, "State variables must not null");
    this.address = contractAddress;
    this.version = version;
    this.language = language;
    this.functions = unmodifiableList(functions);
    this.stateVariables = unmodifiableList(stateVariables);
  }

  /**
   * Find a contract function with the given function name.
   *
   * @param functionName function name to find
   * @return {@code ContractFunction} if found. Otherwise, null
   */
  public ContractFunction findFunction(final String functionName) {
    ContractFunction foundFunction = null;
    for (final ContractFunction contractFunction : functions) {
      if (contractFunction.getName().equals(functionName)) {
        foundFunction = contractFunction;
        break;
      }
    }
    return foundFunction;
  }

  public ContractInvocationWithNothing newInvocationBuilder() {
    return new ContractInterface.InvocationBuilder(this);
  }

  public interface ContractInvocationWithNothing {
    ContractInvocationWithReady function(String functionName);
  }

  public interface ContractInvocationWithReady extends hera.util.Builder<ContractInvocation> {
    ContractInvocationWithReady args(Object... args);

    ContractInvocationWithReady amount(Aer amount);

    ContractInvocationWithReady delegateFee(boolean delegateFee);
  }

  @RequiredArgsConstructor
  protected static class InvocationBuilder
      implements ContractInvocationWithNothing, ContractInvocationWithReady {

    @NonNull
    protected final ContractInterface contractInterface;

    protected ContractFunction function;

    protected Object[] args = new Object[0];

    protected Aer amount = Aer.EMPTY;

    protected boolean delegateFee = false;

    @Override
    public ContractInvocationWithReady function(final String functionName) {
      this.function = contractInterface.findFunction(functionName);
      if (null == this.function) {
        throw new HerajException(
            "Cannot find function from interface [name: " + functionName + "]");
      }
      return this;
    }

    @Override
    public ContractInvocationWithReady args(final Object... args) {
      if (null != args) {
        this.args = args;
      }
      return this;
    }

    @Override
    public ContractInvocationWithReady amount(final Aer amount) {
      this.amount = amount;
      return this;
    }

    @Override
    public ContractInvocationWithReady delegateFee(boolean delegateFee) {
      if (false == this.function.isFeeDelegation()) {
        throw new HerajException("Target function cannot delegate fee");
      }
      this.delegateFee = delegateFee;
      return this;
    }

    @Override
    public hera.api.model.ContractInvocation build() {
      return new ContractInvocation(contractInterface.getAddress(), function, asList(args), amount,
          delegateFee);
    }

  }

}