Source: src/js/models/sysmeta/ReplicaList.js

define([
  "common/ValueUtilities",
  "common/ValidationUtilities",
  "models/sysmeta/Replica",
], (ValueUtilities, ValidationUtilities, Replica) => {
  const { requireNonNegativeInteger } = ValueUtilities;
  const { fixParsedValue } = ValidationUtilities;
  /**
   * Collection of replica entries used by System Metadata.
   * @class ReplicaList
   * @augments Array
   * @classcategory Models/SysMeta
   * @since 2.39.0
   */
  class ReplicaList extends Array {
    /**
     * Create a ReplicaList instance.
     * @param {Array<Replica|object>} [replicas] Replica entries to normalize.
     */
    constructor(replicas = []) {
      super();

      Array.from(replicas).forEach((replica) => {
        this.push(
          replica instanceof Replica
            ? new Replica(replica.toJSON())
            : new Replica(replica),
        );
      });
    }

    /**
     * Append one or more replicas to the list.
     * @param {...(Replica|object)} replicas Replica entries to append.
     * @returns {number} The new array length.
     */
    push(...replicas) {
      return super.push(
        ...replicas.map((replica) =>
          replica instanceof Replica ? replica : new Replica(replica),
        ),
      );
    }

    /**
     * Append one replica entry.
     * @param {Replica|object} replica Replica entry to append.
     * @returns {ReplicaList} The same list instance.
     */
    add(replica) {
      this.push(replica);
      return this;
    }

    /**
     * Replace one replica entry.
     * @param {number} index Replica index to replace.
     * @param {Replica|object} replica Replacement replica.
     * @returns {ReplicaList} The same list instance.
     * @throws {Error} When index is invalid or does not identify a replica
     */
    replace(index, replica) {
      const normalizedIndex = requireNonNegativeInteger(index);
      if (normalizedIndex >= this.length) {
        throw new Error(
          `ReplicaList.replace could not find replica at index ${normalizedIndex}.`,
        );
      }
      this[normalizedIndex] =
        replica instanceof Replica ? replica : new Replica(replica);
      return this;
    }

    /**
     * Remove one replica entry.
     * @param {number} index Replica index to remove.
     * @returns {ReplicaList} The same list instance.
     * @throws {Error} When index is invalid or does not identify a replica
     */
    remove(index) {
      const normalizedIndex = requireNonNegativeInteger(index);
      if (normalizedIndex >= this.length) {
        throw new Error(
          `ReplicaList.remove could not find replica at index ${normalizedIndex}.`,
        );
      }
      this.splice(normalizedIndex, 1);
      return this;
    }

    /**
     * Remove all replicas.
     * @returns {ReplicaList} The same list instance.
     */
    clear() {
      this.length = 0;
      return this;
    }

    /**
     * Normalize the replica list in place.
     * @returns {ReplicaList} The same list instance.
     */
    normalize() {
      const normalizedReplicas = Array.from(this, (replica) =>
        replica instanceof Replica
          ? new Replica(replica.toJSON())
          : new Replica(replica),
      );
      this.length = 0;
      super.push(...normalizedReplicas);
      return this;
    }

    /**
     * Coerce unknown input into a ReplicaList instance.
     * @param {ReplicaList|Array<Replica|object>|Replica|object|null|undefined} value
     * Replica-like input.
     * @returns {ReplicaList} Normalized replica list instance.
     */
    static fromValue(value) {
      if (value === undefined || value === null) return new ReplicaList();
      if (value instanceof ReplicaList) return new ReplicaList(value);
      return new ReplicaList(Array.isArray(value) ? value : [value]);
    }

    /**
     * Parse replica elements while salvaging valid entries.
     * @param {Element[]|null|undefined} elements Replica XML elements.
     * @param {Array<object>} [parseWarnings] Lossy parse warnings.
     * @param {string} [path] Field path used in warnings and validation.
     * @returns {ReplicaList} Parsed replica list instance.
     */
    static parse(elements, parseWarnings = [], path = "replica") {
      const replicas = Array.from(elements || [])
        .map((element, index) =>
          Replica.parse(element, parseWarnings, `${path}[${index}]`),
        )
        .filter(Boolean);

      const replicaList = new ReplicaList(replicas);
      return fixParsedValue({
        value: replicaList,
        path,
        parseWarnings,
        invalidMessage:
          "Ignored invalid replica content while parsing system metadata.",
        validate: (candidate) => candidate.validate(path),
        fallback: () => new ReplicaList(),
      });
    }

    /**
     * Validate each replica in the list.
     * @param {string} [path] Base path used in validation errors. Defaults to
     * `replica`.
     * @returns {Array<object>} Validation errors for invalid replicas.
     */
    validate(path = "replica") {
      const errors = [];
      this.forEach((replica, index) => {
        errors.push(...replica.validate(`${path}[${index}]`));
      });
      return errors;
    }

    /**
     * Return a JSON-serializable representation of the replica list.
     * @returns {Array<object>} Plain replica data.
     */
    toJSON() {
      return Array.from(this, (replica) => replica.toJSON());
    }
  }

  return ReplicaList;
});