Csv.java

// Generated by delombok at Mon Nov 18 07:27:48 UTC 2024
package de.larssh.utils.text;

import static java.util.Collections.emptyList;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import de.larssh.utils.Finals;
import de.larssh.utils.Nullables;
import de.larssh.utils.collection.ProxiedList;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
 * This class represents CSV data, consisting of rows of values, implementing
 * {@code List<String>} for convenience reasons.
 *
 * <p>
 * The first row is referenced as <i>header</i> row and {@link CsvRow} instances
 * allow to retrieve their values by the column's CSV header value.
 *
 * <p>
 * Instances of this class can be marked unmodifiable using
 * {@link #unmodifiable()}. Afterwards applying modifying actions on that
 * instance results in an {@link UnsupportedOperationException}.
 */
@SuppressWarnings("PMD.ShortClassName")
public class Csv extends ProxiedList<CsvRow> {
	/**
	 * The default CSV escape character
	 */
	public static final char DEFAULT_ESCAPER = Finals.constant('\"');
	/**
	 * The default CSV separator character
	 */
	public static final char DEFAULT_SEPARATOR = Finals.constant(',');

	/**
	 * Parses the CSV data given by {@code reader}, starting at the current
	 * position.
	 *
	 * @param reader    a {@link Reader} as CSV data input
	 * @param separator the CSV separator character
	 * @param escaper   the CSV escaping character
	 * @return an object representing the parsed CSV data
	 * @throws IllegalArgumentException on illegal {@code separator} or
	 *                                  {@code escaper} value
	 * @throws IOException              if an I/O error occurs
	 */
	public static Csv parse(final Reader reader, final char separator, final char escaper) throws IOException {
		return new CsvParser(separator, escaper).parse(reader);
	}

	/**
	 * Flag specifying if this instance can be modified
	 *
	 * @param modifiable flag
	 */
	private boolean modifiable = true;

	/**
	 * This class represents CSV data, consisting of rows of values, implementing
	 * {@code List<String>} for convenience reasons.
	 *
	 * <p>
	 * The first row is referenced as <i>header</i> row and {@link CsvRow} instances
	 * allow to retrieve their values by the column's CSV header value.
	 */
	public Csv() {
		super(new ArrayList<>());
	}

	/**
	 * This class represents CSV data, consisting of rows of values, implementing
	 * {@code List<String>} for convenience reasons.
	 *
	 * <p>
	 * The first row is referenced as <i>header</i> row and {@link CsvRow} instances
	 * allow to retrieve their values by the column's CSV header value.
	 *
	 * @param initialCapacity an initial capacity of the internal list
	 * @throws IllegalArgumentException if the specified initial capacity is
	 *                                  negative
	 */
	public Csv(final int initialCapacity) {
		super(new ArrayList<>(initialCapacity));
	}

	/**
	 * This class represents CSV data, consisting of rows of values, implementing
	 * {@code List<String>} for convenience reasons.
	 *
	 * <p>
	 * The first row is referenced as <i>header</i> row and {@link CsvRow} instances
	 * allow to retrieve their values by the column's CSV header value.
	 *
	 * @param data a collection containing CSV rows to be added
	 */
	@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
	@SuppressFBWarnings(value = "PCOA_PARTIALLY_CONSTRUCTED_OBJECT_ACCESS", justification = "passing this object to CsvRow constructor shouldn\'t be a problem")
	public Csv(final List<? extends List<String>> data) {
		this(data.size());
		for (final List<String> row : Nullables.orElseThrow(data)) {
			super.add(new CsvRow(this, super.size(), Nullables.orElseThrow(row)));
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean add(@Nullable final CsvRow element) {
		return add((List<String>) Nullables.orElseThrow(element));
	}

	/**
	 * Adds the specified CSV row to the this CSV object.
	 *
	 * @param element the CSV row to be added
	 * @return {@code true}
	 * @throws UnsupportedOperationException if this CSV object is unmodifiable
	 */
	public boolean add(final List<String> element) {
		return super.add(new CsvRow(this, size(), element));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unused")
	public void add(final int index, @Nullable final CsvRow element) {
		throw new UnsupportedOperationException();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean addAll(@Nullable final Collection<? extends CsvRow> collection) {
		return addAllList(Nullables.orElseThrow(collection));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unused")
	public boolean addAll(final int index, @Nullable final Collection<? extends CsvRow> collection) {
		throw new UnsupportedOperationException();
	}

	/**
	 * Adds all of the CSV rows in {@code collection} to this CSV object.
	 *
	 * <p>
	 * The behavior of this operation is undefined if the specified collection is
	 * modified while the operation is in progress. (This implies, that the behavior
	 * of this call is undefined if the specified collection is this CSV object, and
	 * this CSV object is non-empty.)
	 *
	 * @param collection the collection containing CSV rows to be added
	 * @return {@code true} if this CSV object changed as a result of the call, else
	 *         {@code false}
	 * @throws UnsupportedOperationException if this CSV object is unmodifiable
	 */
	public boolean addAllList(final Collection<? extends List<String>> collection) {
		boolean modified = false;
		for (final List<String> row : collection) {
			modified |= add(row);
		}
		return modified;
	}

	/**
	 * {@inheritDoc}
	 */
	@NonNull
	@Override
	public CsvRow get(final int index) {
		return getWrappedForRead().get(index);
	}

	/**
	 * Returns the headers of this CSV object. Headers are specified by the first
	 * row. If this CSV object is empty, an empty list is returned.
	 *
	 * @return the headers of this CSV object
	 */
	public List<String> getHeaders() {
		return isEmpty() ? emptyList() : get(0);
	}

	/**
	 * {@inheritDoc}
	 */
	@NonNull
	@Override
	public CsvRow remove(final int index) {
		if (index == size() - 1) {
			return Nullables.orElseThrow(super.remove(index));
		}
		throw new UnsupportedOperationException();
	}

	/**
	 * {@inheritDoc}
	 */
	@NonNull
	@Override
	public CsvRow set(final int index, @Nullable final CsvRow element) {
		return getWrappedIfModifiable().set(index, new CsvRow(this, index, Nullables.orElseThrow(element)));
	}

	/**
	 * Returns a valid CSV string representation of this object using the default
	 * separator and escaper.
	 *
	 * @return a valid CSV representation of this object using default separator and
	 *         escaper
	 */
	@Override
	public String toString() {
		return toString(DEFAULT_SEPARATOR, Csv.DEFAULT_ESCAPER);
	}

	/**
	 * Returns a valid CSV string representation of this object using
	 * {@code separator} and {@code escaper}.
	 *
	 * <p>
	 * This is a short-hand method for
	 * {@link StringConverters#encodeCsv(Collection, char, char)}.
	 *
	 * @param separator the CSV separator
	 * @param escaper   the CSV value escaper
	 * @return a valid CSV representation of this object using {@code separator} and
	 *         {@code escaper}
	 */
	public String toString(final char separator, final char escaper) {
		return StringConverters.encodeCsv(this, separator, escaper);
	}

	/**
	 * Marks the CSV object as unmodifiable
	 *
	 * @return this objects
	 */
	public Csv unmodifiable() {
		modifiable = false;
		return this;
	}

	/**
	 * Flag specifying if this instance can be modified
	 *
	 * @return {@code true} if this instance is modifiable, else {@code false}
	 */
	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	public boolean isModifiable() {
		return this.modifiable;
	}
}