Job.java

// Generated by delombok at Mon Apr 14 16:48:01 UTC 2025
package de.larssh.jes;

import static java.util.Arrays.asList;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import de.larssh.utils.OptionalInts;
import de.larssh.utils.text.Strings;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
 * Value object containing a jobs status information. Depending on the objects
 * creating code not all fields are present.
 *
 * <p>
 * Two {@code Job} objects are equal if their IDs are equal. Therefore two
 * objects with the same ID, but different job status (e.g. one older and one
 * up-to-date object) are still equal!
 */
@SuppressWarnings({"PMD.DataClass", "PMD.GodClass", "PMD.ShortClassName"})
public class Job {
	/**
	 * The jobs ID, must not be empty
	 */
	@SuppressWarnings("PMD.ShortVariable")
	private final String id;
	/**
	 * The jobs name
	 *
	 * <p>
	 * Depending on the objects creating code this might be a filter value,
	 * eventually containing {@link JesClient#FILTER_WILDCARD}.
	 */
	private final String name;
	/**
	 * The jobs status
	 */
	private final JobStatus status;
	/**
	 * The jobs owner
	 *
	 * <p>
	 * Depending on the objects creating code this might be a filter value,
	 * eventually containing {@link JesClient#FILTER_WILDCARD}.
	 */
	private final String owner;
	/**
	 * The jobs JES spool class
	 */
	private final Optional<String> jesClass;
	/**
	 * The jobs result code
	 *
	 * <p>
	 * This field can be present for {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}) only. It cannot be present as long as
	 * {@code abendCode} is present.
	 */
	private final OptionalInt resultCode;
	/**
	 * The jobs abend code
	 *
	 * <p>
	 * This field can be present for {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}) only. It cannot be present as long as
	 * {@code resultCode} is present.
	 */
	private final Optional<String> abendCode;
	/**
	 * The jobs flags
	 */
	private final Set<JobFlag> flags;
	/**
	 * List of job output details
	 *
	 * <p>
	 * Can be filled by
	 * {@link #createOutput(int, String, int, Optional, Optional, Optional)} and
	 * must be empty for status other than {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}).
	 *
	 * @return list of job outputs
	 */
	private final List<JobOutput> outputs = new ArrayList<>();

	/**
	 * This constructor creates a {@link Job} in its simplest form. String
	 * parameters are trimmed and converted to upper case.
	 *
	 * @param jobId  the jobs ID, must not be empty
	 * @param name   the jobs name or a name filter value
	 * @param status the jobs status or {@link JobStatus#ALL}
	 * @param owner  the jobs owner or an owner filter value
	 * @throws JobFieldInconsistentException on inconsistent field value
	 */
	@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Fields are initialized prior throwing exceptions")
	public Job(final String jobId, final String name, final JobStatus status, final String owner) {
		this(jobId, name, status, owner, Optional.empty(), OptionalInt.empty(), Optional.empty());
	}

	/**
	 * This constructor creates a {@link Job} allowing to set any field. String
	 * parameters are trimmed and converted to upper case.
	 *
	 * @param jobId      the jobs ID, must not be empty
	 * @param name       the jobs name or a name filter value
	 * @param status     the jobs status or {@link JobStatus#ALL}
	 * @param owner      the jobs owner or an owner filter value
	 * @param jesClass   the jobs JES class
	 * @param resultCode the jobs result code, when held (finished)
	 * @param abendCode  the jobs abend code, when held (finished)
	 * @param flags      the jobs flags
	 * @throws JobFieldInconsistentException on inconsistent field value
	 */
	@SuppressWarnings("checkstyle:ParameterNumber")
	@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Fields are initialized prior throwing exceptions")
	public Job(final String jobId, final String name, final JobStatus status, final String owner, final Optional<String> jesClass, final OptionalInt resultCode, final Optional<String> abendCode, final JobFlag... flags) {
		id = Strings.toUpperCaseNeutral(jobId.trim());
		this.name = Strings.toUpperCaseNeutral(name.trim());
		this.status = status;
		this.owner = Strings.toUpperCaseNeutral(owner.trim());
		this.jesClass = jesClass.map(String::trim).map(Strings::toUpperCaseNeutral);
		this.abendCode = abendCode.map(String::trim).map(Strings::toUpperCaseNeutral);
		this.resultCode = resultCode;
		this.flags = flags.length == 0 ? emptySet() : unmodifiableSet(EnumSet.copyOf(asList(flags)));
		validate();
	}

	/**
	 * Creates a {@link JobOutput} and adds it to the list of job outputs. Creating
	 * of job outputs for status other than {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}) is prohibited. String parameters are trimmed and
	 * converted to upper case.
	 *
	 * @param index         the job outputs index inside the jobs list of outputs
	 *                      (starting at 1)
	 * @param outputName    the job outputs data division name
	 * @param length        the job outputs content length
	 * @param step          the job outputs step name
	 * @param procedureStep the job outputs procedure step name
	 * @param outputClass   the output class
	 * @return created job output
	 * @throws JobFieldInconsistentException on inconsistent field value
	 */
	public JobOutput createOutput(final int index, final String outputName, final int length, final Optional<String> step, final Optional<String> procedureStep, final Optional<String> outputClass) {
		if (getStatus() != JobStatus.OUTPUT && getStatus() != JobStatus.ALL) {
			throw new JobFieldInconsistentException("Job outputs for status other than OUTPUT (and ALL) cannot be created. Status: %s", getStatus());
		}
		final JobOutput output = new JobOutput(this, index, outputName, length, step, procedureStep, outputClass);
		outputs.add(output);
		return output;
	}

	/**
	 * Returns the job output details referenced by {@code name} or
	 * {@link Optional#empty()} if no such job output exists.
	 *
	 * @param name job output name to search for
	 * @return job output details referenced by {@code name} or
	 *         {@link Optional#empty()} if no such job output exists
	 */
	public Optional<JobOutput> getOutput(final String name) {
		final String trimmedName = name.trim();
		return getOutputs().stream().filter(output -> output.getName().equalsIgnoreCase(trimmedName)).findFirst();
	}

	/**
	 * List of job output details
	 *
	 * <p>
	 * Can be filled by
	 * {@link #createOutput(int, String, int, Optional, Optional, Optional)} and
	 * must be empty for status other than {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}).
	 *
	 * @return list of job output details
	 */
	public List<JobOutput> getOutputs() {
		return unmodifiableList(outputs);
	}

	/**
	 * Validates fields to be set in a consistent way.
	 *
	 * @throws JobFieldInconsistentException on inconsistent field value
	 */
	@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
	private void validate() {
		if (id.isEmpty()) {
			throw new JobFieldInconsistentException("Job ID must be filled, but is empty.");
		}
		if (jesClass.filter(String::isEmpty).isPresent()) {
			throw new JobFieldInconsistentException("JES class must not be empty if present.");
		}
		if (resultCode.isPresent() && abendCode.isPresent()) {
			throw new JobFieldInconsistentException("Result Code and Abend Code must not be present at the same time.");
		}
		if (OptionalInts.filter(resultCode, r -> r < 0).isPresent()) {
			throw new JobFieldInconsistentException("Result Code must not be less than zero.");
		}
		if (abendCode.filter(String::isEmpty).isPresent()) {
			throw new JobFieldInconsistentException("Abend Code must not be empty if present.");
		}
		if (status != JobStatus.OUTPUT && status != JobStatus.ALL) {
			if (resultCode.isPresent()) {
				throw new JobFieldInconsistentException(Strings.format("Result Code must not be present for status [%s].", status));
			}
			if (abendCode.isPresent()) {
				throw new JobFieldInconsistentException(Strings.format("Abend Code must not be present for status [%s].", status));
			}
		}
	}

	/**
	 * The jobs ID, must not be empty
	 *
	 * @return job id
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public String getId() {
		return this.id;
	}

	/**
	 * The jobs name
	 *
	 * <p>
	 * Depending on the objects creating code this might be a filter value,
	 * eventually containing {@link JesClient#FILTER_WILDCARD}.
	 *
	 * @return job name
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public String getName() {
		return this.name;
	}

	/**
	 * The jobs status
	 *
	 * @return job status
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public JobStatus getStatus() {
		return this.status;
	}

	/**
	 * The jobs owner
	 *
	 * <p>
	 * Depending on the objects creating code this might be a filter value,
	 * eventually containing {@link JesClient#FILTER_WILDCARD}.
	 *
	 * @return job owner
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public String getOwner() {
		return this.owner;
	}

	/**
	 * The jobs JES spool class
	 *
	 * @return job class
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public Optional<String> getJesClass() {
		return this.jesClass;
	}

	/**
	 * The jobs result code
	 *
	 * <p>
	 * This field can be present for {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}) only. It cannot be present as long as
	 * {@code abendCode} is present.
	 *
	 * @return result code
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public OptionalInt getResultCode() {
		return this.resultCode;
	}

	/**
	 * The jobs abend code
	 *
	 * <p>
	 * This field can be present for {@link JobStatus#OUTPUT} (and
	 * {@link JobStatus#ALL}) only. It cannot be present as long as
	 * {@code resultCode} is present.
	 *
	 * @return abend code
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public Optional<String> getAbendCode() {
		return this.abendCode;
	}

	/**
	 * The jobs flags
	 *
	 * @return flags
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public Set<JobFlag> getFlags() {
		return this.flags;
	}

	@edu.umd.cs.findbugs.annotations.NonNull
	@java.lang.Override
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public java.lang.String toString() {
		return "Job(id=" + this.getId() + ", name=" + this.getName() + ", status=" + this.getStatus() + ", owner=" + this.getOwner() + ", jesClass=" + this.getJesClass() + ", resultCode=" + this.getResultCode() + ", abendCode=" + this.getAbendCode() + ", flags=" + this.getFlags() + ", outputs=" + this.getOutputs() + ")";
	}

	@java.lang.Override
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public boolean equals(@edu.umd.cs.findbugs.annotations.Nullable final java.lang.Object o) {
		if (o == this) return true;
		if (!(o instanceof Job)) return false;
		final Job other = (Job) o;
		if (!other.canEqual((java.lang.Object) this)) return false;
		final java.lang.Object this$id = this.getId();
		final java.lang.Object other$id = other.getId();
		if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false;
		return true;
	}

	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	protected boolean canEqual(@edu.umd.cs.findbugs.annotations.Nullable final java.lang.Object other) {
		return other instanceof Job;
	}

	@java.lang.Override
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public int hashCode() {
		final int PRIME = 59;
		int result = 1;
		final java.lang.Object $id = this.getId();
		result = result * PRIME + ($id == null ? 43 : $id.hashCode());
		return result;
	}
}