// Autogenerated code. Do not modify.
package org.inferred.freebuilder.processor.source;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;
import java.util.function.IntUnaryOperator;
import java.util.function.UnaryOperator;
import javax.annotation.Generated;

/**
 * Auto-generated superclass of {@link TypeUsage.Builder}, derived from the API of {@link
 * TypeUsage}.
 */
@Generated("org.inferred.freebuilder.processor.Processor")
abstract class TypeUsage_Builder {

  /**
   * Creates a new builder using {@code value} as a template.
   *
   * <p>If {@code value} is a partial, the builder will return more partials.
   */
  public static TypeUsage.Builder from(TypeUsage value) {
    if (value instanceof Rebuildable) {
      return ((Rebuildable) value).toBuilder();
    } else {
      return new TypeUsage.Builder().mergeFrom(value);
    }
  }

  private enum Property {
    START("start"),
    END("end"),
    TYPE("type"),
    ;

    private final String name;

    private Property(String name) {
      this.name = name;
    }

    @Override
    public String toString() {
      return name;
    }
  }

  private int start;
  private int end;
  private QualifiedName type;
  // Store a nullable object instead of an Optional. Escape analysis then
  // allows the JVM to optimize away the Optional objects created by and
  // passed to our API.
  private QualifiedName scope = null;
  private final EnumSet<Property> _unsetProperties = EnumSet.allOf(Property.class);

  /**
   * Sets the value to be returned by {@link TypeUsage#start()}.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder start(int start) {
    this.start = start;
    _unsetProperties.remove(Property.START);
    return (TypeUsage.Builder) this;
  }

  /**
   * Replaces the value to be returned by {@link TypeUsage#start()} by applying {@code mapper} to it
   * and using the result.
   *
   * @return this {@code Builder} object
   * @throws NullPointerException if {@code mapper} is null
   * @throws IllegalStateException if the field has not been set
   */
  public TypeUsage.Builder mapStart(IntUnaryOperator mapper) {
    Objects.requireNonNull(mapper);
    return start(mapper.applyAsInt(start()));
  }

  /**
   * Returns the value that will be returned by {@link TypeUsage#start()}.
   *
   * @throws IllegalStateException if the field has not been set
   */
  public int start() {
    Preconditions.checkState(!_unsetProperties.contains(Property.START), "start not set");
    return start;
  }

  /**
   * Sets the value to be returned by {@link TypeUsage#end()}.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder end(int end) {
    this.end = end;
    _unsetProperties.remove(Property.END);
    return (TypeUsage.Builder) this;
  }

  /**
   * Replaces the value to be returned by {@link TypeUsage#end()} by applying {@code mapper} to it
   * and using the result.
   *
   * @return this {@code Builder} object
   * @throws NullPointerException if {@code mapper} is null
   * @throws IllegalStateException if the field has not been set
   */
  public TypeUsage.Builder mapEnd(IntUnaryOperator mapper) {
    Objects.requireNonNull(mapper);
    return end(mapper.applyAsInt(end()));
  }

  /**
   * Returns the value that will be returned by {@link TypeUsage#end()}.
   *
   * @throws IllegalStateException if the field has not been set
   */
  public int end() {
    Preconditions.checkState(!_unsetProperties.contains(Property.END), "end not set");
    return end;
  }

  /**
   * Sets the value to be returned by {@link TypeUsage#type()}.
   *
   * @return this {@code Builder} object
   * @throws NullPointerException if {@code type} is null
   */
  public TypeUsage.Builder type(QualifiedName type) {
    this.type = Objects.requireNonNull(type);
    _unsetProperties.remove(Property.TYPE);
    return (TypeUsage.Builder) this;
  }

  /**
   * Replaces the value to be returned by {@link TypeUsage#type()} by applying {@code mapper} to it
   * and using the result.
   *
   * @return this {@code Builder} object
   * @throws NullPointerException if {@code mapper} is null or returns null
   * @throws IllegalStateException if the field has not been set
   */
  public TypeUsage.Builder mapType(UnaryOperator<QualifiedName> mapper) {
    Objects.requireNonNull(mapper);
    return type(mapper.apply(type()));
  }

  /**
   * Returns the value that will be returned by {@link TypeUsage#type()}.
   *
   * @throws IllegalStateException if the field has not been set
   */
  public QualifiedName type() {
    Preconditions.checkState(!_unsetProperties.contains(Property.TYPE), "type not set");
    return type;
  }

  /**
   * Sets the value to be returned by {@link TypeUsage#scope()}.
   *
   * @return this {@code Builder} object
   * @throws NullPointerException if {@code scope} is null
   */
  public TypeUsage.Builder scope(QualifiedName scope) {
    this.scope = Objects.requireNonNull(scope);
    return (TypeUsage.Builder) this;
  }

  /**
   * Sets the value to be returned by {@link TypeUsage#scope()}.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder scope(Optional<? extends QualifiedName> scope) {
    if (scope.isPresent()) {
      return scope(scope.get());
    } else {
      return clearScope();
    }
  }

  /**
   * Sets the value to be returned by {@link TypeUsage#scope()}.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder nullableScope(QualifiedName scope) {
    if (scope != null) {
      return scope(scope);
    } else {
      return clearScope();
    }
  }

  /**
   * If the value to be returned by {@link TypeUsage#scope()} is present, replaces it by applying
   * {@code mapper} to it and using the result.
   *
   * <p>If the result is null, clears the value.
   *
   * @return this {@code Builder} object
   * @throws NullPointerException if {@code mapper} is null
   */
  public TypeUsage.Builder mapScope(UnaryOperator<QualifiedName> mapper) {
    return scope(scope().map(mapper));
  }

  /**
   * Sets the value to be returned by {@link TypeUsage#scope()} to {@link Optional#empty()
   * Optional.empty()}.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder clearScope() {
    scope = null;
    return (TypeUsage.Builder) this;
  }

  /** Returns the value that will be returned by {@link TypeUsage#scope()}. */
  public Optional<QualifiedName> scope() {
    return Optional.ofNullable(scope);
  }

  /**
   * Copies values from {@code value}, skipping empty optionals.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder mergeFrom(TypeUsage value) {
    TypeUsage_Builder defaults = new TypeUsage.Builder();
    if (defaults._unsetProperties.contains(Property.START) || value.start() != defaults.start()) {
      start(value.start());
    }
    if (defaults._unsetProperties.contains(Property.END) || value.end() != defaults.end()) {
      end(value.end());
    }
    if (defaults._unsetProperties.contains(Property.TYPE)
        || !Objects.equals(value.type(), defaults.type())) {
      type(value.type());
    }
    value.scope().ifPresent(this::scope);
    return (TypeUsage.Builder) this;
  }

  /**
   * Copies values from {@code template}, skipping empty optionals and unset properties.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder mergeFrom(TypeUsage.Builder template) {
    // Upcast to access private fields; otherwise, oddly, we get an access violation.
    TypeUsage_Builder base = template;
    TypeUsage_Builder defaults = new TypeUsage.Builder();
    if (!base._unsetProperties.contains(Property.START)
        && (defaults._unsetProperties.contains(Property.START)
            || template.start() != defaults.start())) {
      start(template.start());
    }
    if (!base._unsetProperties.contains(Property.END)
        && (defaults._unsetProperties.contains(Property.END) || template.end() != defaults.end())) {
      end(template.end());
    }
    if (!base._unsetProperties.contains(Property.TYPE)
        && (defaults._unsetProperties.contains(Property.TYPE)
            || !Objects.equals(template.type(), defaults.type()))) {
      type(template.type());
    }
    template.scope().ifPresent(this::scope);
    return (TypeUsage.Builder) this;
  }

  /**
   * Resets the state of this builder.
   *
   * @return this {@code Builder} object
   */
  public TypeUsage.Builder clear() {
    TypeUsage_Builder defaults = new TypeUsage.Builder();
    start = defaults.start;
    end = defaults.end;
    type = defaults.type;
    scope = defaults.scope;
    _unsetProperties.clear();
    _unsetProperties.addAll(defaults._unsetProperties);
    return (TypeUsage.Builder) this;
  }

  /**
   * Returns a newly-created {@link TypeUsage} based on the contents of this {@code Builder}.
   *
   * @throws IllegalStateException if any field has not been set
   */
  public TypeUsage build() {
    Preconditions.checkState(_unsetProperties.isEmpty(), "Not set: %s", _unsetProperties);
    return new Value(this);
  }

  /**
   * Returns a newly-created partial {@link TypeUsage} for use in unit tests. State checking will
   * not be performed. Unset properties will throw an {@link UnsupportedOperationException} when
   * accessed via the partial object.
   *
   * <p>The builder returned by {@link TypeUsage.Builder#from(TypeUsage)} will propagate the partial
   * status of its input, overriding {@link TypeUsage.Builder#build() build()} to return another
   * partial. This allows for robust tests of modify-rebuild code.
   *
   * <p>Partials should only ever be used in tests. They permit writing robust test cases that won't
   * fail if this type gains more application-level constraints (e.g. new required fields) in
   * future. If you require partially complete values in production code, consider using a Builder.
   */
  @VisibleForTesting()
  public TypeUsage buildPartial() {
    return new Partial(this);
  }

  private abstract static class Rebuildable implements TypeUsage {
    public abstract Builder toBuilder();
  }

  private static final class Value extends Rebuildable {
    private final int start;
    private final int end;
    private final QualifiedName type;
    // Store a nullable object instead of an Optional. Escape analysis then
    // allows the JVM to optimize away the Optional objects created by our
    // getter method.
    private final QualifiedName scope;

    private Value(TypeUsage_Builder builder) {
      this.start = builder.start;
      this.end = builder.end;
      this.type = builder.type;
      this.scope = builder.scope;
    }

    @Override
    public int start() {
      return start;
    }

    @Override
    public int end() {
      return end;
    }

    @Override
    public QualifiedName type() {
      return type;
    }

    @Override
    public Optional<QualifiedName> scope() {
      return Optional.ofNullable(scope);
    }

    @Override
    public Builder toBuilder() {
      TypeUsage_Builder builder = new Builder();
      builder.start = start;
      builder.end = end;
      builder.type = type;
      builder.scope = scope;
      builder._unsetProperties.clear();
      return (Builder) builder;
    }

    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof Value)) {
        return false;
      }
      Value other = (Value) obj;
      return start == other.start
          && end == other.end
          && Objects.equals(type, other.type)
          && Objects.equals(scope, other.scope);
    }

    @Override
    public int hashCode() {
      return Objects.hash(start, end, type, scope);
    }

    @Override
    public String toString() {
      StringBuilder result =
          new StringBuilder("TypeUsage{start=")
              .append(start)
              .append(", end=")
              .append(end)
              .append(", type=")
              .append(type);
      if (scope != null) {
        result.append(", scope=").append(scope);
      }
      return result.append("}").toString();
    }
  }

  private static final class Partial extends Rebuildable {
    private final int start;
    private final int end;
    private final QualifiedName type;
    // Store a nullable object instead of an Optional. Escape analysis then
    // allows the JVM to optimize away the Optional objects created by our
    // getter method.
    private final QualifiedName scope;
    private final EnumSet<Property> _unsetProperties;

    Partial(TypeUsage_Builder builder) {
      this.start = builder.start;
      this.end = builder.end;
      this.type = builder.type;
      this.scope = builder.scope;
      this._unsetProperties = builder._unsetProperties.clone();
    }

    @Override
    public int start() {
      if (_unsetProperties.contains(Property.START)) {
        throw new UnsupportedOperationException("start not set");
      }
      return start;
    }

    @Override
    public int end() {
      if (_unsetProperties.contains(Property.END)) {
        throw new UnsupportedOperationException("end not set");
      }
      return end;
    }

    @Override
    public QualifiedName type() {
      if (_unsetProperties.contains(Property.TYPE)) {
        throw new UnsupportedOperationException("type not set");
      }
      return type;
    }

    @Override
    public Optional<QualifiedName> scope() {
      return Optional.ofNullable(scope);
    }

    private static class PartialBuilder extends Builder {
      @Override
      public TypeUsage build() {
        return buildPartial();
      }
    }

    @Override
    public Builder toBuilder() {
      TypeUsage_Builder builder = new PartialBuilder();
      builder.start = start;
      builder.end = end;
      builder.type = type;
      builder.scope = scope;
      builder._unsetProperties.clear();
      builder._unsetProperties.addAll(_unsetProperties);
      return (Builder) builder;
    }

    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof Partial)) {
        return false;
      }
      Partial other = (Partial) obj;
      return start == other.start
          && end == other.end
          && Objects.equals(type, other.type)
          && Objects.equals(scope, other.scope)
          && Objects.equals(_unsetProperties, other._unsetProperties);
    }

    @Override
    public int hashCode() {
      return Objects.hash(start, end, type, scope, _unsetProperties);
    }

    @Override
    public String toString() {
      StringBuilder result = new StringBuilder("partial TypeUsage{");
      String separator = "";
      if (!_unsetProperties.contains(Property.START)) {
        result.append("start=").append(start);
        separator = ", ";
      }
      if (!_unsetProperties.contains(Property.END)) {
        result.append(separator).append("end=").append(end);
        separator = ", ";
      }
      if (!_unsetProperties.contains(Property.TYPE)) {
        result.append(separator).append("type=").append(type);
        separator = ", ";
      }
      if (scope != null) {
        result.append(separator).append("scope=").append(scope);
      }
      return result.append("}").toString();
    }
  }
}
