Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2015, Haiyang Li.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.landawn.abacus.util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.MayReturnNull;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.parser.KryoParser;
import com.landawn.abacus.parser.ParserFactory;
import com.landawn.abacus.util.If.OrElse;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.function.IntBiFunction;
import com.landawn.abacus.util.function.IntBiPredicate;
import com.landawn.abacus.util.function.TriFunction;
import com.landawn.abacus.util.function.TriPredicate;
import com.landawn.abacus.util.stream.IntStream;
import com.landawn.abacus.util.stream.ObjIteratorEx;
import com.landawn.abacus.util.stream.Stream;
/**
* This is the main class for the Sheet data structure.
* A Sheet is a two-dimensional table that can store data in cells, each identified by a row key and a column key.
* The row keys are of type {@code R}, the column keys are of type {@code C}, and the values stored in the cells are of type {@code V}.
* This class implements the Cloneable interface, meaning it can create and return a copy of itself.
*
*
* @param the type of the row keys
* @param the type of the column keys
* @param the type of the values stored in the cells
*
* @see com.landawn.abacus.util.DataSet
*/
public final class Sheet implements Cloneable {
static final KryoParser kryoParser = ParserFactory.isKryoAvailable() ? ParserFactory.createKryoParser() : null;
private final Set _rowKeySet; //NOSONAR
private final Set _columnKeySet; //NOSONAR
private BiMap _rowKeyIndexMap; //NOSONAR
private BiMap _columnKeyIndexMap; //NOSONAR
private List> _columnList; //NOSONAR
private boolean _isInitialized = false; //NOSONAR
private boolean _isFrozen = false; //NOSONAR
/**
* Default constructor for the Sheet class.
* Initializes an empty Sheet with no row keys and no column keys.
*/
public Sheet() {
this(N.emptyList(), N.emptyList());
}
/**
* Constructs a new Sheet with the specified row keys and column keys.
* The row keys and column keys are used to identify the cells in the Sheet.
* The Sheet is initially empty, meaning it contains no values.
*
* @param rowKeySet the collection of row keys for the Sheet
* @param columnKeySet the collection of column keys for the Sheet
* @throws IllegalArgumentException if any of the row keys or column keys are null
*/
public Sheet(final Collection rowKeySet, final Collection columnKeySet) throws IllegalArgumentException {
N.checkArgument(!N.anyNull(rowKeySet), "Row key can't be null");
N.checkArgument(!N.anyNull(columnKeySet), "Column key can't be null");
_rowKeySet = N.newLinkedHashSet(rowKeySet);
_columnKeySet = N.newLinkedHashSet(columnKeySet);
}
/**
* Constructs a new Sheet with the specified row keys, column keys, and initial data.
* The row keys and column keys are used to identify the cells in the Sheet.
* The initial data is provided as a two-dimensional array where each inner array represents a row of data.
*
* @param rowKeySet the collection of row keys for the Sheet
* @param columnKeySet the collection of column keys for the Sheet
* @param rows the initial data for the Sheet, where each inner array represents a row of data
* @throws IllegalArgumentException if any of the row keys or column keys are {@code null}, or if the dimensions of the initial data do not match the provided row keys and column keys
*/
public Sheet(final Collection rowKeySet, final Collection columnKeySet, final Object[][] rows) throws IllegalArgumentException {
this(rowKeySet, columnKeySet);
final int rowLength = this.rowLength();
final int columnLength = this.columnLength();
if (N.notEmpty(rows)) {
N.checkArgument(rows.length == rowLength, "The length of array is not equal to size of row/column key set"); //NOSONAR
for (final Object[] e : rows) {
N.checkArgument(e.length == columnLength, "The length of array is not equal to size of row/column key set");
}
initIndexMap();
_columnList = new ArrayList<>(columnLength);
for (int i = 0; i < columnLength; i++) {
final List column = new ArrayList<>(rowLength);
for (int j = 0; j < rowLength; j++) {
column.add((V) rows[j][i]);
}
_columnList.add(column);
}
_isInitialized = true;
}
}
@SuppressWarnings("rawtypes")
private static final Sheet EMPTY_SHEET = new Sheet<>(N.emptyList(), N.emptyList(), new Object[0][0]);
static {
EMPTY_SHEET.freeze();
}
/**
* Returns an empty immutable {@code Sheet}.
* This method is useful when you need an empty Sheet for initialization purposes.
* The returned Sheet is immutable, meaning no modifications (such as adding or removing rows/columns, or changing values) are allowed.
*
* @param the type of the row keys
* @param the type of the column keys
* @param the type of the values stored in the cells
* @return an empty immutable {@code Sheet}
*/
public static Sheet empty() {
return EMPTY_SHEET;
}
/**
* Constructs a new Sheet with the specified row keys, column keys, and initial data.
* The row keys and column keys are used to identify the cells in the Sheet.
* The initial data is provided as a two-dimensional array where each inner array represents a row of data.
*
* @param the type of the row keys
* @param the type of the column keys
* @param the type of the values stored in the cells
* @param rowKeySet the collection of row keys for the Sheet
* @param columnKeySet the collection of column keys for the Sheet
* @param rows the initial data for the Sheet, where each inner array represents a row of data
* @return a new Sheet with the specified row keys, column keys, and initial data
* @throws IllegalArgumentException if any of the row keys or column keys are {@code null}, or if the dimensions of the initial data do not match the provided row keys and column keys
*/
public static Sheet rows(final Collection rowKeySet, final Collection columnKeySet, final Object[][] rows)
throws IllegalArgumentException {
return new Sheet<>(rowKeySet, columnKeySet, rows);
}
/**
* Constructs a new Sheet with the specified row keys, column keys, and initial data.
* The row keys and column keys are used to identify the cells in the Sheet.
* The initial data is provided as a collection of collections where each inner collection represents a row of data.
*
* @param the type of the row keys
* @param the type of the column keys
* @param the type of the values stored in the cells
* @param rowKeySet the collection of row keys for the Sheet
* @param columnKeySet the collection of column keys for the Sheet
* @param rows the initial data for the Sheet, where each inner collection represents a row of data
* @return a new Sheet with the specified row keys, column keys, and initial data
* @throws IllegalArgumentException if any of the row keys or column keys are {@code null}, or if the dimensions of the initial data do not match the provided row keys and column keys
*/
public static Sheet rows(final Collection rowKeySet, final Collection columnKeySet,
final Collection extends Collection extends V>> rows) throws IllegalArgumentException {
final Sheet instance = new Sheet<>(rowKeySet, columnKeySet);
final int rowLength = instance.rowLength();
final int columnLength = instance.columnLength();
if (N.notEmpty(rows)) {
N.checkArgument(rows.size() == rowLength, "The size of collection is not equal to size of row/column key set"); //NOSONAR
for (final Collection extends V> e : rows) {
N.checkArgument(e.size() == columnLength, "The size of collection is not equal to size of row/column key set");
}
instance.initIndexMap();
instance._columnList = new ArrayList<>(columnLength);
for (int i = 0; i < columnLength; i++) {
instance._columnList.add(new ArrayList<>(rowLength));
}
for (final Collection extends V> row : rows) {
final Iterator extends V> iter = row.iterator();
for (int i = 0; i < columnLength; i++) {
instance._columnList.get(i).add(iter.next());
}
}
instance._isInitialized = true;
}
return instance;
}
/**
* Constructs a new Sheet with the specified row keys, column keys, and initial data.
* The row keys and column keys are used to identify the cells in the Sheet.
* The initial data is provided as a two-dimensional array where each inner array represents a column of data.
*
* @param the type of the row keys
* @param the type of the column keys
* @param the type of the values stored in the cells
* @param rowKeySet the collection of row keys for the Sheet
* @param columnKeySet the collection of column keys for the Sheet
* @param columns the initial data for the Sheet, where each inner array represents a column of data
* @return a new Sheet with the specified row keys, column keys, and initial data
* @throws IllegalArgumentException if any of the row keys or column keys are {@code null}, or if the dimensions of the initial data do not match the provided row keys and column keys
*/
public static Sheet columns(final Collection rowKeySet, final Collection columnKeySet, final Object[][] columns)
throws IllegalArgumentException {
final Sheet instance = new Sheet<>(rowKeySet, columnKeySet);
final int rowLength = instance.rowLength();
final int columnLength = instance.columnLength();
if (N.notEmpty(columns)) {
N.checkArgument(columns.length == columnLength, "The length of array is not equal to size of row/column key set");
for (final Object[] e : columns) {
N.checkArgument(e.length == rowLength, "The length of array is not equal to size of row/column key set");
}
instance.initIndexMap();
instance._columnList = new ArrayList<>(columnLength);
for (final Object[] column : columns) {
instance._columnList.add(new ArrayList<>((List) Arrays.asList(column)));
}
instance._isInitialized = true;
}
return instance;
}
/**
* Constructs a new Sheet with the specified row keys, column keys, and initial data.
* The row keys and column keys are used to identify the cells in the Sheet.
* The initial data is provided as a collection of collections where each inner collection represents a column of data.
*
* @param the type of the row keys
* @param the type of the column keys
* @param the type of the values stored in the cells
* @param rowKeySet the collection of row keys for the Sheet
* @param columnKeySet the collection of column keys for the Sheet
* @param columns the initial data for the Sheet, where each inner collection represents a column of data
* @return a new Sheet with the specified row keys, column keys, and initial data
* @throws IllegalArgumentException if any of the row keys or column keys are {@code null}, or if the dimensions of the initial data do not match the provided row keys and column keys
*/
public static Sheet columns(final Collection rowKeySet, final Collection columnKeySet,
final Collection extends Collection extends V>> columns) throws IllegalArgumentException {
final Sheet instance = new Sheet<>(rowKeySet, columnKeySet);
final int rowLength = instance.rowLength();
final int columnLength = instance.columnLength();
if (N.notEmpty(columns)) {
N.checkArgument(columns.size() == columnLength, "The size of collection is not equal to size of row/column key set");
for (final Collection extends V> e : columns) {
N.checkArgument(e.size() == rowLength, "The size of collection is not equal to size of row/column key set");
}
instance.initIndexMap();
instance._columnList = new ArrayList<>(columnLength);
for (final Collection extends V> column : columns) {
instance._columnList.add(new ArrayList<>(column));
}
instance._isInitialized = true;
}
return instance;
}
/**
* Returns the immutable set of row keys in the Sheet.
* The row keys are used to identify the rows in the Sheet.
*
* @return an ImmutableSet of row keys
*/
public ImmutableSet rowKeySet() {
return ImmutableSet.wrap(_rowKeySet);
}
/**
* Returns the immutable set of column keys in the Sheet.
* The column keys are used to identify the columns in the Sheet.
*
* @return a ImmutableSet of column keys
*/
public ImmutableSet columnKeySet() {
return ImmutableSet.wrap(_columnKeySet);
}
/**
* Retrieves the value stored in the cell identified by the specified row key and column key.
*
* @param rowKey the row key of the cell
* @param columnKey the column key of the cell
* @return the value stored in the cell, or {@code null} if the cell does not exist
* @throws IllegalArgumentException if the specified rowKey or columnKey does not exist in the Sheet
*/
@MayReturnNull
public V get(final R rowKey, final C columnKey) throws IllegalArgumentException {
if (_isInitialized) {
final int rowIndex = getRowIndex(rowKey);
final int columnIndex = getColumnIndex(columnKey);
return get(rowIndex, columnIndex);
} else {
checkRowKey(rowKey);
checkColumnKey(columnKey);
return null;
}
}
/**
* Retrieves the value stored in the cell identified by the specified row index and column index.
*
* @param rowIndex the index of the row
* @param columnIndex the index of the column
* @return the value stored in the cell, or {@code null} if the cell does not exist
* @throws IndexOutOfBoundsException if the specified indices are out of bounds
*/
@MayReturnNull
public V get(final int rowIndex, final int columnIndex) throws IndexOutOfBoundsException {
checkRowIndex(rowIndex);
checkColumnIndex(columnIndex);
if (_isInitialized) {
return _columnList.get(columnIndex).get(rowIndex);
} else {
return null;
}
}
/**
* Retrieves the value stored in the cell identified by the specified Point.
* The Point represents the row index and column index of the cell.
*
* @param point the Point of the cell
* @return the value stored in the cell, or {@code null} if the cell does not exist
* @throws IndexOutOfBoundsException if the specified indices are out of bounds
*/
@Beta
public V get(final Point point) throws IndexOutOfBoundsException {
return get(point.rowIndex, point.columnIndex);
}
/**
* Inserts or updates a value in the cell identified by the specified row key and column key.
* If the cell already contains a value, the existing value is replaced with the new value.
* If the cell does not exist, a new cell is created with the specified keys and value.
*
* @param rowKey the row key of the cell
* @param columnKey the column key of the cell
* @param value the new value to be stored in the cell
* @return the previous value stored in the cell
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the specified rowKey or columnKey does not exist in the Sheet
*/
public V put(final R rowKey, final C columnKey, final V value) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int rowIndex = getRowIndex(rowKey);
final int columnIndex = getColumnIndex(columnKey);
init();
// if (!containsRow(rowKey)) {
// addRow(rowKey, null);
// }
//
// if (!containsColumn(columnKey)) {
// addColumn(columnKey, null);
// }
return put(rowIndex, columnIndex, value);
}
/**
* Inserts or updates a value in the cell identified by the specified row index and column index.
* If the cell already contains a value, the existing value is replaced with the new value.
* If the cell does not exist, a new cell is created with the specified indices and value.
*
* @param rowIndex the index of the row
* @param columnIndex the index of the column
* @param value the new value to be stored in the cell
* @return the previous value stored in the cell
* @throws IllegalStateException if the Sheet is frozen
* @throws IndexOutOfBoundsException if the specified indices are out of bounds
*/
public V put(final int rowIndex, final int columnIndex, final V value) throws IllegalStateException, IndexOutOfBoundsException {
checkFrozen();
checkRowIndex(rowIndex);
checkColumnIndex(columnIndex);
init();
final V previousValue = _columnList.get(columnIndex).get(rowIndex);
_columnList.get(columnIndex).set(rowIndex, value);
return previousValue;
}
/**
* Inserts or updates a value in the cell identified by the specified Point.
* the Point represents the row index and column index of the cell.
* If the cell already contains a value, the existing value is replaced with the new value.
* If the cell does not exist, a new cell is created at the specified point with the provided value.
*
* @param point the Point of the cell
* @param value the new value to be stored in the cell
* @return the previous value stored in the cell
* @throws IllegalStateException if the Sheet is frozen
* @throws IndexOutOfBoundsException if the specified indices are out of bounds
*/
@Beta
public V put(final Point point, final V value) throws IllegalStateException, IndexOutOfBoundsException {
return put(point.rowIndex, point.columnIndex, value);
}
/**
* Inserts or updates all values from the specified source Sheet into this Sheet.
* The source Sheet must have the same or a subset of the row keys and column keys as this Sheet.
* If a cell in this Sheet already contains a value, the existing value is replaced with the new value from the source Sheet.
* If a cell does not exist, a new cell is created with the keys and value from the source Sheet.
*
* @param source the source Sheet from which to get the new values
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the source Sheet contains row keys or column keys that are not present in this Sheet
*/
public void putAll(final Sheet extends R, ? extends C, ? extends V> source) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
if (!this.rowKeySet().containsAll(source.rowKeySet())) {
throw new IllegalArgumentException(source.rowKeySet() + " are not all included in this sheet with row key set: " + this.rowKeySet());
}
if (!this.columnKeySet().containsAll(source.columnKeySet())) {
throw new IllegalArgumentException(source.columnKeySet() + " are not all included in this sheet with column key set: " + this.columnKeySet());
}
final Sheet tmp = (Sheet) source;
for (final R r : tmp.rowKeySet()) {
for (final C c : tmp.columnKeySet()) {
// this.put(r, c, tmp.get(r, c));
put(getRowIndex(r), getColumnIndex(c), tmp.get(r, c));
}
}
}
/**
* Removes the value stored in the cell identified by the specified row key and column key.
*
* @param rowKey the row key of the cell
* @param columnKey the column key of the cell
* @return the value that was stored in the cell
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the specified rowKey or columnKey does not exist in the Sheet
*/
@MayReturnNull
public V remove(final R rowKey, final C columnKey) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
if (_isInitialized) {
final int rowIndex = getRowIndex(rowKey);
final int columnIndex = getColumnIndex(columnKey);
return remove(rowIndex, columnIndex);
} else {
checkRowKey(rowKey);
checkColumnKey(columnKey);
return null;
}
}
/**
* Removes the value stored in the cell identified by the specified row index and column index.
*
* @param rowIndex the index of the row
* @param columnIndex the index of the column
* @return the value that was stored in the cell
* @throws IllegalStateException if the Sheet is frozen
* @throws IndexOutOfBoundsException if the specified indices are out of bounds
*/
@MayReturnNull
public V remove(final int rowIndex, final int columnIndex) throws IllegalStateException, IndexOutOfBoundsException {
checkFrozen();
checkRowIndex(rowIndex);
checkColumnIndex(columnIndex);
if (_isInitialized) {
return _columnList.get(columnIndex).set(rowIndex, null);
} else {
return null;
}
}
/**
* Removes the value stored in the cell identified by the specified Point.
* the Point represents the row index and column index of the cell.
*
* @param point the Point of the cell
* @return the value that was stored in the cell
* @throws IllegalStateException if the Sheet is frozen
* @throws IndexOutOfBoundsException if the specified indices are out of bounds
*/
@Beta
public V remove(final Point point) throws IllegalStateException, IndexOutOfBoundsException {
return remove(point.rowIndex, point.columnIndex);
}
/**
* Checks if the Sheet contains a cell identified by the specified row key and column key.
* If the cell exists, this method returns {@code true}. Otherwise, it returns {@code false}.
*
* @param rowKey the row key of the cell
* @param columnKey the column key of the cell
* @return {@code true} if the cell exists, {@code false} otherwise
*/
public boolean contains(final R rowKey, final C columnKey) {
return _rowKeySet.contains(rowKey) && _columnKeySet.contains(columnKey);
}
/**
* Checks if the Sheet contains a cell identified by the specified row key, column key, and value.
* If the cell exists and contains the specified value, this method returns {@code true}. Otherwise, it returns {@code false}.
*
* @param rowKey the row key of the cell
* @param columnKey the column key of the cell
* @param value the value to check in the cell
* @return {@code true} if the cell exists and contains the specified value, {@code false} otherwise
*/
public boolean contains(final R rowKey, final C columnKey, final Object value) {
return N.equals(get(rowKey, columnKey), value);
}
/**
* Checks if the Sheet contains a cell with the specified value.
* If a cell with the specified value exists, this method returns {@code true}. Otherwise, it returns {@code false}.
*
* @param value the value to check in the cells
* @return {@code true} if a cell with the specified value exists, {@code false} otherwise
*/
public boolean containsValue(final Object value) {
// if (value == null) {
// for (R r : rowKeySet()) {
// for (C c : columnKeySet()) {
// if (this.get(r, c) == null) {
// return true;
// }
// }
// }
// } else {
// for (R r : rowKeySet()) {
// for (C c : columnKeySet()) {
// if (value.equals(this.get(r, c))) {
// return true;
// }
// }
// }
// }
//
// return false;
if (_isInitialized) {
for (final List column : _columnList) {
//noinspection SuspiciousMethodCalls
if (column.contains(value)) {
return true;
}
}
return false;
} else {
return value == null;
}
}
/**
* Retrieves all the values in the row identified by the specified row key.
*
* @param rowKey the row key of the row
* @return an ImmutableList of values in the row
* @throws IllegalArgumentException if the row key does not exist in the Sheet
*/
public ImmutableList getRow(final R rowKey) throws IllegalArgumentException {
final int columnLength = columnLength();
final List row = new ArrayList<>(columnLength);
if (_isInitialized) {
final int rowIndex = getRowIndex(rowKey);
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
row.add(_columnList.get(columnIndex).get(rowIndex));
}
} else {
checkRowKey(rowKey);
N.fill(row, 0, columnLength, null);
}
return ImmutableList.wrap(row);
}
/**
* Sets the values for a specific row in the Sheet.
* The row is identified by the provided row key and the values are provided as a collection.
* The order of the values in the collection should match the order of the column keys in the Sheet.
*
* @param rowKey the key of the row to be set
* @param row the collection of values to be set in the row
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the row key does not exist in the Sheet or the provided collection is not empty and its size does not match the number of columns in the Sheet
*/
public void setRow(final R rowKey, final Collection extends V> row) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int rowIndex = getRowIndex(rowKey);
final int columnLength = columnLength();
if (N.notEmpty(row) && row.size() != columnLength) {
throw new IllegalArgumentException("The size of specified row: " + row.size() + " doesn't match the size of column key set: " + columnLength); //NOSONAR
}
init();
if (N.isEmpty(row)) {
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).set(rowIndex, null);
}
} else {
final Iterator extends V> iter = row.iterator();
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).set(rowIndex, iter.next());
}
}
}
/**
* Adds a new row to the Sheet.
* The row is identified by the provided row key and the values are provided as a collection.
* The order of the values in the collection should match the order of the column keys in the Sheet.
*
* @param rowKey the key of the row to be added
* @param row the collection of values to be added in the row
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the row key already exists in the Sheet or the provided collection is not empty and its size does not match the number of columns in the Sheet
*/
public void addRow(final R rowKey, final Collection extends V> row) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
if (_rowKeySet.contains(rowKey)) {
throw new IllegalArgumentException("Row '" + rowKey + "' already existed"); //NOSONAR
}
final int rowLength = rowLength();
final int columnLength = columnLength();
if (N.notEmpty(row) && row.size() != columnLength) {
throw new IllegalArgumentException("The size of specified row: " + row.size() + " doesn't match the size of column key set: " + columnLength);
}
init();
_rowKeySet.add(rowKey);
_rowKeyIndexMap.put(rowKey, rowLength);
if (N.isEmpty(row)) {
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).add(null);
}
} else {
final Iterator extends V> iter = row.iterator();
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).add(iter.next());
}
}
}
/**
* Inserts a new row at the specified index in the Sheet.
* The row is identified by the provided row key and the values are provided as a collection.
* The order of the values in the collection should match the order of the column keys in the Sheet.
*
* @param rowIndex the index at which the row should be inserted
* @param rowKey the key of the row to be added
* @param row the collection of values to be added in the row
* @throws IllegalStateException if the Sheet is frozen
* @throws IndexOutOfBoundsException if the specified {@code rowIndex} is less than zero or bigger than row size
* @throws IllegalArgumentException if the row key already exists in the Sheet or the provided collection is not empty and its size does not match the number of columns in the Sheet
*/
public void addRow(final int rowIndex, final R rowKey, final Collection extends V> row)
throws IllegalStateException, IndexOutOfBoundsException, IllegalArgumentException {
checkFrozen();
final int rowLength = rowLength();
final int columnLength = columnLength();
if (rowIndex == rowLength) {
addRow(rowKey, row);
return;
}
if (rowIndex < 0 || rowIndex > rowLength) {
throw new IndexOutOfBoundsException("The new row index " + rowIndex + " is out-of-bounds for row size " + (rowLength + 1));
}
if (_rowKeySet.contains(rowKey)) {
throw new IllegalArgumentException("Row '" + rowKey + "' already existed");
}
if (N.notEmpty(row) && row.size() != columnLength) {
throw new IllegalArgumentException("The size of specified row: " + row.size() + " doesn't match the size of column key set: " + columnLength);
}
init();
final List tmp = new ArrayList<>(rowLength + 1);
tmp.addAll(_rowKeySet);
tmp.add(rowIndex, rowKey);
_rowKeySet.clear();
_rowKeySet.addAll(tmp);
for (int i = _rowKeyIndexMap.size() - 1; i >= rowIndex; i--) {
_rowKeyIndexMap.put(_rowKeyIndexMap.getByValue(i), i + 1);
}
_rowKeyIndexMap.put(rowKey, rowIndex);
if (N.isEmpty(row)) {
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).add(rowIndex, null);
}
} else {
final Iterator extends V> iter = row.iterator();
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).add(rowIndex, iter.next());
}
}
}
/**
* Updates the values in the row identified by the provided row key using the provided function.
* The function takes each value in the row as input and returns the updated value.
*
* @param rowKey the key of the row to be updated
* @param func the function to apply to each value in the row
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the row key does not exist in the Sheet
*/
public void updateRow(final R rowKey, final Function super V, ? extends V> func) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int rowIndex = this.getRowIndex(rowKey);
if (columnLength() > 0) {
this.init();
for (final List column : _columnList) {
column.set(rowIndex, func.apply(column.get(rowIndex)));
}
}
}
/**
* Removes the row identified by the provided row key from the Sheet.
*
* @param rowKey the key of the row to be removed
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the row key does not exist in the Sheet
*/
public void removeRow(final R rowKey) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
checkRowKey(rowKey);
_rowKeySet.remove(rowKey);
if (_rowKeyIndexMap != null) {
final int columnLength = columnLength();
final int newRowSize = rowLength();
final int removedRowIndex = _rowKeyIndexMap.remove(rowKey);
if (removedRowIndex == newRowSize) {
// removed last row.
} else {
for (int i = removedRowIndex; i < newRowSize; i++) {
_rowKeyIndexMap.put(_rowKeyIndexMap.getByValue(i + 1), i);
}
}
if (_isInitialized) {
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
_columnList.get(columnIndex).remove(removedRowIndex); //NOSONAR
}
}
}
}
/**
* Moves the row identified by the provided row key to a new position in the Sheet.
* The new position is specified by the new row index.
*
* @param rowKey the key of the row to be moved
* @param newRowIndex the new index at which the row should be positioned
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the row key does not exist in the Sheet or the new index is out of bounds
* @throws IndexOutOfBoundsException if the new row index is out of bounds
*/
public void moveRow(final R rowKey, final int newRowIndex) throws IllegalStateException, IllegalArgumentException, IndexOutOfBoundsException {
checkFrozen();
this.checkRowIndex(newRowIndex);
final int rowIndex = this.getRowIndex(rowKey);
final List tmp = new ArrayList<>(rowLength());
tmp.addAll(_rowKeySet);
tmp.add(newRowIndex, tmp.remove(rowIndex));
_rowKeySet.clear();
_rowKeySet.addAll(tmp);
_rowKeyIndexMap = null;
if (_isInitialized && _columnList.size() > 0) {
for (final List column : _columnList) {
column.add(newRowIndex, column.remove(rowIndex));
}
}
}
/**
* Swaps the positions of two rows in the Sheet.
* The rows are identified by the provided row keys.
*
* @param rowKeyA the key of the first row to be swapped
* @param rowKeyB the key of the second row to be swapped
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the row keys do not exist in the Sheet
*/
public void swapRows(final R rowKeyA, final R rowKeyB) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int rowIndexA = this.getRowIndex(rowKeyA);
final int rowIndexB = this.getRowIndex(rowKeyB);
final List tmp = new ArrayList<>(rowLength());
tmp.addAll(_rowKeySet);
final R tmpRowKeyA = tmp.get(rowIndexA);
tmp.set(rowIndexA, tmp.get(rowIndexB));
tmp.set(rowIndexB, tmpRowKeyA);
_rowKeySet.clear();
_rowKeySet.addAll(tmp);
_rowKeyIndexMap.forcePut(tmp.get(rowIndexA), rowIndexA);
_rowKeyIndexMap.forcePut(tmp.get(rowIndexB), rowIndexB);
if (_isInitialized && _columnList.size() > 0) {
V tmpVal = null;
for (final List column : _columnList) {
tmpVal = column.get(rowIndexA);
column.set(rowIndexA, column.get(rowIndexB));
column.set(rowIndexB, tmpVal);
}
}
}
/**
* Renames a row in the Sheet.
* The row to be renamed is identified by the provided old row key and the new name is provided as the new row key.
*
* @param rowKey the old key of the row to be renamed
* @param newRowKey the new key for the row
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the old row key does not exist in the Sheet or the new row key is already in use
*/
public void renameRow(final R rowKey, final R newRowKey) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
checkRowKey(rowKey);
if (_rowKeySet.contains(newRowKey)) {
throw new IllegalArgumentException("Invalid new row key: " + N.toString(newRowKey) + ". It's already in the row key set.");
}
final int rowIndex = this.getRowIndex(rowKey);
final List tmp = new ArrayList<>(_rowKeySet);
tmp.set(rowIndex, newRowKey);
_rowKeySet.clear();
_rowKeySet.addAll(tmp);
if (N.notEmpty(_rowKeyIndexMap)) {
_rowKeyIndexMap.put(newRowKey, _rowKeyIndexMap.remove(rowKey));
}
}
/**
* Checks if the Sheet contains a row identified by the specified row key.
* If the row exists, this method returns {@code true}. Otherwise, it returns {@code false}.
*
* @param rowKey the row key to check
* @return {@code true} if the row exists, {@code false} otherwise
*/
public boolean containsRow(final R rowKey) {
return _rowKeySet.contains(rowKey);
}
/**
* Retrieves a map representing a row in the Sheet.
* The row is identified by the provided row key.
* The map's keys are the column keys and the values are the values stored in the cells of the row.
*
* @param rowKey the row key of the row
* @return a Map where the keys are the column keys and the values are the values stored in the cells of the row
* @throws IllegalArgumentException if the row key does not exist in the Sheet
*/
public Map row(final R rowKey) throws IllegalArgumentException {
final int columnLength = columnLength();
final Map rowMap = N.newLinkedHashMap(columnLength);
if (_isInitialized) {
final int rowIndex = getRowIndex(rowKey);
int columnIndex = 0;
for (final C columnKey : this.columnKeySet()) {
rowMap.put(columnKey, _columnList.get(columnIndex++).get(rowIndex));
}
} else {
checkRowKey(rowKey);
for (final C columnKey : this.columnKeySet()) {
rowMap.put(columnKey, null);
}
}
return rowMap;
}
/**
* Retrieves a map representing all rows in the Sheet.
* Each entry in the map corresponds to a row in the Sheet.
* The map's keys are the row keys and the values are maps where the keys are the column keys and the values are the values stored in the cells of the row.
* An empty map is returned if the Sheet is empty.
*
* @return a Map where the keys are the row keys and the values are maps representing the rows. In these maps, the keys are the column keys and the values are the values stored in the cells of the row.
*/
public Map> rowMap() {
final Map> result = N.newLinkedHashMap(this.rowKeySet().size());
for (final R rowKey : this.rowKeySet()) {
result.put(rowKey, row(rowKey));
}
return result;
}
/**
* Retrieves all the values in the column identified by the provided column key.
*
* @param columnKey the column key of the column
* @return an ImmutableList of values in the column
* @throws IllegalArgumentException if the column key does not exist in the Sheet
*/
public ImmutableList getColumn(final C columnKey) throws IllegalArgumentException {
// if (_initialized) {
// column = _columnList.get(getColumnIndex(columnKey));
// } else {
// final int rowLength = rowLength();
// checkColumnKey(columnKey);
// column = new ArrayList<>(rowLength);
// N.fill(column, 0, rowLength, null);
// }
if (!_isInitialized) {
init();
}
final List column = _columnList.get(getColumnIndex(columnKey));
return ImmutableList.wrap(column);
}
/**
* Sets the values for a specific column in the Sheet.
* The column is identified by the provided column key and the values are provided as a collection.
* The order of the values in the collection should match the order of the row keys in the Sheet.
*
* @param columnKey the key of the column to be set
* @param column the collection of values to be set in the column
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the column key does not exist in the Sheet or the provided collection is not empty and its size does not match the number of rows in the Sheet
*/
public void setColumn(final C columnKey, final Collection extends V> column) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int columnIndex = getColumnIndex(columnKey);
final int rowLength = rowLength();
if (N.notEmpty(column) && column.size() != rowLength) {
throw new IllegalArgumentException("The size of specified column: " + column.size() + " doesn't match the size of row key set: " + rowLength); //NOSONAR
}
init();
if (N.isEmpty(column)) {
N.fill(_columnList.get(columnIndex), 0, rowLength, null);
} else {
_columnList.set(columnIndex, new ArrayList<>(column));
}
}
/**
* Adds a new column to the Sheet.
* The column is identified by the provided column key and the values are provided as a collection.
* The order of the values in the collection should match the order of the row keys in the Sheet.
*
* @param columnKey the key of the column to be added
* @param column the collection of values to be added in the column
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the column key already exists in the Sheet or the provided collection is not empty and its size does not match the number of rows in the Sheet
*/
public void addColumn(final C columnKey, final Collection extends V> column) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
if (_columnKeySet.contains(columnKey)) {
throw new IllegalArgumentException("Column '" + columnKey + "' already existed");
}
final int rowLength = rowLength();
final int columnLength = columnLength();
if (N.notEmpty(column) && column.size() != rowLength) {
throw new IllegalArgumentException("The size of specified column: " + column.size() + " doesn't match the size of row key set: " + rowLength);
}
init();
_columnKeySet.add(columnKey);
_columnKeyIndexMap.put(columnKey, columnLength);
if (N.isEmpty(column)) {
final List newColumn = new ArrayList<>();
N.fill(newColumn, 0, rowLength, null);
_columnList.add(newColumn);
} else {
_columnList.add(new ArrayList<>(column));
}
}
/**
* Inserts a new column at the specified index in the Sheet.
* The column is identified by the provided column key and the values are provided as a collection.
* The order of the values in the collection should match the order of the row keys in the Sheet.
*
* @param columnIndex the index at which the column should be inserted
* @param columnKey the key of the column to be added
* @param column the collection of values to be added in the column
* @throws IllegalStateException if the Sheet is frozen
* @throws IndexOutOfBoundsException if the specified {@code columnIndex} is less than zero or bigger than column size
* @throws IllegalArgumentException if the column key already exists in the Sheet or the provided collection is not empty and its size does not match the number of rows in the Sheet
*/
public void addColumn(final int columnIndex, final C columnKey, final Collection extends V> column)
throws IllegalStateException, IndexOutOfBoundsException, IllegalArgumentException {
checkFrozen();
final int rowLength = rowLength();
final int columnLength = columnLength();
if (columnIndex == columnLength) {
addColumn(columnKey, column);
return;
}
if (columnIndex < 0 || columnIndex > columnLength) {
throw new IndexOutOfBoundsException("The new column index " + columnIndex + " is out-of-bounds for column size " + (columnLength + 1));
}
if (_columnKeySet.contains(columnKey)) {
throw new IllegalArgumentException("Column '" + columnKey + "' already existed");
}
if (N.notEmpty(column) && column.size() != rowLength) {
throw new IllegalArgumentException("The size of specified column: " + column.size() + " doesn't match the size of row key set: " + rowLength);
}
init();
final List tmp = new ArrayList<>(columnLength + 1);
tmp.addAll(_columnKeySet);
tmp.add(columnIndex, columnKey);
_columnKeySet.clear();
_columnKeySet.addAll(tmp);
for (int i = _columnKeyIndexMap.size() - 1; i >= columnIndex; i--) {
_columnKeyIndexMap.put(_columnKeyIndexMap.getByValue(i), i + 1);
}
_columnKeyIndexMap.put(columnKey, columnIndex);
if (N.isEmpty(column)) {
final List newColumn = new ArrayList<>();
N.fill(newColumn, 0, rowLength, null);
_columnList.add(columnIndex, newColumn);
} else {
_columnList.add(columnIndex, new ArrayList<>(column));
}
}
/**
* Updates the values in the column identified by the provided column key using the provided function.
* The function takes each value in the column as input and returns the updated value.
*
* @param columnKey the key of the column to be updated
* @param func the function to apply to each value in the column
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the column key does not exist in the Sheet
*/
public void updateColumn(final C columnKey, final Function super V, ? extends V> func) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int columnIndex = this.getColumnIndex(columnKey);
if (rowLength() > 0) {
this.init();
final int rowLength = rowLength();
final List column = _columnList.get(columnIndex);
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
column.set(rowIndex, func.apply(column.get(rowIndex)));
}
}
}
/**
* Removes the column identified by the provided column key from the Sheet.
*
* @param columnKey the key of the column to be removed
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the column key does not exist in the Sheet
*/
public void removeColumn(final C columnKey) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
checkColumnKey(columnKey);
_columnKeySet.remove(columnKey);
if (_columnKeyIndexMap != null) {
final int newColumnLength = columnLength();
final int removedColumnIndex = _columnKeyIndexMap.remove(columnKey);
if (removedColumnIndex == newColumnLength) {
// removed the last column
} else {
for (int i = removedColumnIndex; i < newColumnLength; i++) {
_columnKeyIndexMap.put(_columnKeyIndexMap.getByValue(i + 1), i);
}
}
if (_isInitialized) {
_columnList.remove(removedColumnIndex);
}
}
}
/**
* Moves the column identified by the provided column key to a new position in the Sheet.
* The new position is specified by the new column index.
*
* @param columnKey the key of the column to be moved
* @param newColumnIndex the new index at which the column should be positioned
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the column key does not exist
* @throws IndexOutOfBoundsException if the new column index is out of bounds
*/
public void moveColumn(final C columnKey, final int newColumnIndex) throws IllegalStateException, IllegalArgumentException, IndexOutOfBoundsException {
checkFrozen();
final int columnIndex = this.getColumnIndex(columnKey);
this.checkColumnIndex(newColumnIndex);
final List tmp = new ArrayList<>(columnLength());
tmp.addAll(_columnKeySet);
tmp.add(newColumnIndex, tmp.remove(columnIndex));
_columnKeySet.clear();
_columnKeySet.addAll(tmp);
_columnKeyIndexMap = null;
if (_isInitialized && _columnList.size() > 0) {
_columnList.add(newColumnIndex, _columnList.remove(columnIndex));
}
}
/**
* Swaps the positions of two columns in the Sheet.
* The columns are identified by the provided column keys.
*
* @param columnKeyA the key of the first column to be swapped
* @param columnKeyB the key of the second column to be swapped
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the column keys do not exist in the Sheet
*/
public void swapColumns(final C columnKeyA, final C columnKeyB) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
final int columnIndexA = getColumnIndex(columnKeyA);
final int columnIndexB = getColumnIndex(columnKeyB);
final List tmp = new ArrayList<>(rowLength());
tmp.addAll(_columnKeySet);
final C tmpColumnKeyA = tmp.get(columnIndexA);
tmp.set(columnIndexA, tmp.get(columnIndexB));
tmp.set(columnIndexB, tmpColumnKeyA);
_columnKeySet.clear();
_columnKeySet.addAll(tmp);
_columnKeyIndexMap.forcePut(tmp.get(columnIndexA), columnIndexA);
_columnKeyIndexMap.forcePut(tmp.get(columnIndexB), columnIndexB);
if (_isInitialized && _columnList.size() > 0) {
final List tmpColumnA = _columnList.get(columnIndexA);
_columnList.set(columnIndexA, _columnList.get(columnIndexB));
_columnList.set(columnIndexB, tmpColumnA);
}
}
/**
* Renames a column in the Sheet.
* The column to be renamed is identified by the provided old column key and the new name is provided as the new column key.
*
* @param columnKey the old key of the column to be renamed
* @param newColumnKey the new key for the column
* @throws IllegalStateException if the Sheet is frozen
* @throws IllegalArgumentException if the old column key does not exist in the Sheet or the new column key is already in use
*/
public void renameColumn(final C columnKey, final C newColumnKey) throws IllegalStateException, IllegalArgumentException {
checkFrozen();
this.checkColumnKey(columnKey);
if (_columnKeySet.contains(newColumnKey)) {
throw new IllegalArgumentException("Invalid new column key: " + N.toString(newColumnKey) + ". It's already in the column key set.");
}
final int columnIndex = this.getColumnIndex(columnKey);
final List tmp = new ArrayList<>(_columnKeySet);
tmp.set(columnIndex, newColumnKey);
_columnKeySet.clear();
_columnKeySet.addAll(tmp);
if (N.notEmpty(_columnKeyIndexMap)) {
_columnKeyIndexMap.put(newColumnKey, _columnKeyIndexMap.remove(columnKey));
}
}
/**
* Checks if the Sheet contains a column identified by the specified column key.
* If the column exists, this method returns {@code true}. Otherwise, it returns {@code false}.
*
* @param columnKey the column key to check
* @return {@code true} if the column exists, {@code false} otherwise
*/
public boolean containsColumn(final C columnKey) {
return _columnKeySet.contains(columnKey);
}
/**
* Retrieves a map representing a column in the Sheet.
* The column is identified by the provided column key.
* The map's keys are the row keys and the values are the values stored in the cells of the column.
*
* @param columnKey the column key of the column
* @return a Map where the keys are the row keys and the values are the values stored in the cells of the column
* @throws IllegalArgumentException if the column key does not exist in the Sheet
*/
public Map column(final C columnKey) throws IllegalArgumentException {
final int rowLength = rowLength();
final Map columnMap = N.newLinkedHashMap(rowLength);
if (_isInitialized) {
final int columnIndex = getColumnIndex(columnKey);
final List column = _columnList.get(columnIndex);
int rowIndex = 0;
for (final R rowKey : this.rowKeySet()) {
columnMap.put(rowKey, column.get(rowIndex++));
}
} else {
checkColumnKey(columnKey);
for (final R rowKey : this.rowKeySet()) {
columnMap.put(rowKey, null);
}
}
return columnMap;
}
/**
* Retrieves a map representing all columns in the Sheet.
* Each entry in the map corresponds to a column in the Sheet.
* The map's keys are the column keys and the values are maps where the keys are the row keys and the values are the values stored in the cells of the column.
* An empty map is returned if the Sheet is empty.
*
* @return a Map where the keys are the column keys and the values are maps representing the columns. In these maps, the keys are the row keys and the values are the values stored in the cells of the column.
*/
public Map> columnMap() {
final Map> result = N.newLinkedHashMap(this.columnKeySet().size());
for (final C columnKey : this.columnKeySet()) {
result.put(columnKey, column(columnKey));
}
return result;
}
/**
* Retrieves the number of rows in the Sheet.
*
* @return the number of rows in the Sheet
*/
public int rowLength() {
return _rowKeySet.size();
}
/**
* Retrieves the number of columns in the Sheet.
*
* @return the number of columns in the Sheet
*/
public int columnLength() {
return _columnKeySet.size();
}
// TODO should the method name be "replaceAll"? If change the method name to replaceAll, what about updateColumn/updateRow?
/**
* Updates all values in the sheet using the provided function.
* The function takes each value in the sheet as input and returns the updated value.
*
* @param func The function to apply to each value in the sheet. It takes a value from the sheet as input and returns the updated value.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void updateAll(final Function super V, ? extends V> func) throws IllegalStateException {
checkFrozen();
if (rowLength() > 0 && columnLength() > 0) {
this.init();
final int rowLength = rowLength();
for (final List column : _columnList) {
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
column.set(rowIndex, func.apply(column.get(rowIndex)));
}
}
}
}
/**
* Updates all values in the sheet using the provided function.
* The function takes two integers as input, representing the row and column indices of each value in the sheet, and returns the updated value.
*
* @param func The function to apply to each value in the sheet. It takes the row and column indices of a value from the sheet as input and returns the updated value.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void updateAll(final IntBiFunction extends V> func) throws IllegalStateException {
checkFrozen();
if (rowLength() > 0 && columnLength() > 0) {
this.init();
final int rowLength = rowLength();
int columnIndex = 0;
for (final List column : _columnList) {
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
column.set(rowIndex, func.apply(rowIndex, columnIndex));
}
columnIndex++;
}
}
}
/**
* Updates all values in the sheet using the provided function.
* The function takes three inputs: the row key, the column key, and the current value at that position in the sheet.
* It returns the updated value which is then set at the corresponding position in the sheet.
*
* @param func The function to apply to each value in the sheet. It takes the row key, column key, and a value from the sheet as input and returns the updated value.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void updateAll(final TriFunction super R, ? super C, ? super V, ? extends V> func) throws IllegalStateException {
checkFrozen();
if (rowLength() > 0 && columnLength() > 0) {
this.init();
final int rowLength = rowLength();
int columnIndex = 0;
C columnKey = null;
for (final List column : _columnList) {
columnKey = _columnKeyIndexMap.getByValue(columnIndex);
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
column.set(rowIndex, func.apply(_rowKeyIndexMap.getByValue(rowIndex), columnKey, column.get(rowIndex)));
}
columnIndex++;
}
}
}
/**
* Replaces all values in the sheet that satisfy the provided predicate with the new value.
* The predicate takes each value in the sheet as input and returns a boolean indicating whether the value should be replaced.
*
* @param predicate The predicate to test each value in the sheet. It takes a value from the sheet as input and returns a boolean indicating whether the value should be replaced.
* @param newValue The new value to replace the old values that satisfy the predicate.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void replaceIf(final Predicate super V> predicate, final V newValue) throws IllegalStateException {
checkFrozen();
if (rowLength() > 0 && columnLength() > 0) {
this.init();
final int rowLength = rowLength();
for (final List column : _columnList) {
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
if (predicate.test(column.get(rowIndex))) {
column.set(rowIndex, newValue);
}
}
}
}
}
/**
* Replaces all values in the sheet that satisfy the provided predicate with the new value.
* The predicate takes two integers as input, representing the row and column indices of each value in the sheet, and returns a boolean indicating whether the value should be replaced.
*
* @param predicate The predicate to test each value in the sheet. It takes the row and column indices of a value from the sheet as input and returns a boolean indicating whether the value should be replaced.
* @param newValue The new value to replace the old values that satisfy the predicate.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void replaceIf(final IntBiPredicate predicate, final V newValue) throws IllegalStateException {
checkFrozen();
if (rowLength() > 0 && columnLength() > 0) {
this.init();
final int rowLength = rowLength();
int columnIndex = 0;
for (final List column : _columnList) {
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
if (predicate.test(rowIndex, columnIndex)) {
column.set(rowIndex, newValue);
}
}
columnIndex++;
}
}
}
/**
* Replaces all values in the sheet that satisfy the provided predicate with the new value.
* The predicate takes three inputs: the row key, the column key, and the current value at that position in the sheet.
* It returns a boolean indicating whether the value should be replaced.
*
* @param predicate The predicate to test each value in the sheet. It takes the row key, column key, and a value from the sheet as input and returns a boolean indicating whether the value should be replaced.
* @param newValue The new value to replace the old values that satisfy the predicate.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void replaceIf(final TriPredicate super R, ? super C, ? super V> predicate, final V newValue) throws IllegalStateException {
checkFrozen();
if (rowLength() > 0 && columnLength() > 0) {
this.init();
final int rowLength = rowLength();
int columnIndex = 0;
R rowKey = null;
C columnKey = null;
V val = null;
for (final List column : _columnList) {
columnKey = _columnKeyIndexMap.getByValue(columnIndex);
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
rowKey = _rowKeyIndexMap.getByValue(rowIndex);
val = column.get(rowIndex);
if (predicate.test(rowKey, columnKey, val)) {
column.set(rowIndex, newValue);
}
}
columnIndex++;
}
}
}
/**
* Sorts the rows in the sheet based on the natural ordering of the row keys.
* The natural ordering is the ordering imposed by the objects' own compareTo method.
*
* @throws ClassCastException if the row keys' class does not implement Comparable, or if comparing two row keys throws a ClassCastException.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void sortByRowKey() throws IllegalStateException {
sortByRowKey((Comparator) Comparator.naturalOrder());
}
/**
* Sorts the rows in the sheet based on the row keys using the provided comparator.
* The comparator takes two row keys as input and returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
*
* @param cmp The comparator to determine the order of the row keys. It takes two row keys as input and returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void sortByRowKey(final Comparator super R> cmp) throws IllegalStateException {
checkFrozen();
final int rowLength = rowLength();
final Indexed[] arrayOfPair = new Indexed[rowLength];
final Iterator iter = _rowKeySet.iterator();
for (int rowIndex = 0; rowIndex < rowLength; rowIndex++) {
arrayOfPair[rowIndex] = Indexed.of(iter.next(), rowIndex);
}
final Comparator> pairCmp = createComparatorForIndexedObject(cmp);
N.sort(arrayOfPair, pairCmp);
if (_isInitialized) {
final int columnCount = _columnKeySet.size();
final Set ordered = N.newHashSet(rowLength);
final V[] tempRow = (V[]) new Object[columnCount];
List tmpColumn = null;
for (int i = 0, index = 0; i < rowLength; i++) {
index = arrayOfPair[i].index();
if ((index != i) && !ordered.contains(i)) {
for (int j = 0; j < columnCount; j++) {
tempRow[j] = _columnList.get(j).get(i);
}
int previous = i;
int next = index;
do {
for (int j = 0; j < columnCount; j++) {
tmpColumn = _columnList.get(j);
tmpColumn.set(previous, tmpColumn.get(next));
}
ordered.add(next);
previous = next;
next = arrayOfPair[next].index();
} while (next != i);
for (int j = 0; j < columnCount; j++) {
_columnList.get(j).set(previous, tempRow[j]);
}
ordered.add(i);
}
}
}
final boolean indexedMapInitialized = N.notEmpty(_rowKeyIndexMap);
_rowKeySet.clear();
for (int i = 0; i < rowLength; i++) {
_rowKeySet.add(arrayOfPair[i].value());
if (indexedMapInitialized) {
_rowKeyIndexMap.forcePut(arrayOfPair[i].value(), i);
}
}
}
/**
* Sorts the rows in the sheet based on the values in the specified row using the provided comparator.
* The comparator takes two values from the row as input and returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
*
* @param rowKey The key of the row based on whose values the rows will be sorted.
* @param cmp The comparator to determine the order of the values in the specified row. It takes two values from the row as input and returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void sortByRow(final R rowKey, final Comparator super V> cmp) throws IllegalStateException {
checkFrozen();
if (!_isInitialized) {
return;
}
final int rowIndex = getRowIndex(rowKey);
final int columnLength = columnLength();
final Indexed[] arrayOfPair = new Indexed[columnLength];
for (int columnIndex = 0; columnIndex < columnLength; columnIndex++) {
arrayOfPair[columnIndex] = Indexed.of(_columnList.get(columnIndex).get(rowIndex), columnIndex);
}
if (N.allMatch(arrayOfPair, it -> it.value() == null)) { // All null values in the specified row.
return;
}
final Comparator> pairCmp = createComparatorForIndexedObject(cmp);
N.sort(arrayOfPair, pairCmp);
final Set ordered = N.newHashSet(columnLength);
List tempColumn = null;
for (int i = 0, index = 0; i < columnLength; i++) {
index = arrayOfPair[i].index();
if ((index != i) && !ordered.contains(i)) {
tempColumn = _columnList.get(i);
int previous = i;
int next = index;
do {
_columnList.set(previous, _columnList.get(next));
ordered.add(next);
previous = next;
next = arrayOfPair[next].index();
} while (next != i);
_columnList.set(previous, tempColumn);
ordered.add(i);
}
}
final boolean indexedMapInitialized = N.notEmpty(_columnKeyIndexMap);
final Object[] columnKeys = _columnKeySet.toArray(new Object[columnLength]);
C columnKey = null;
_columnKeySet.clear();
for (int i = 0; i < columnLength; i++) {
columnKey = (C) columnKeys[arrayOfPair[i].index()];
_columnKeySet.add(columnKey);
if (indexedMapInitialized) {
_columnKeyIndexMap.forcePut(columnKey, i);
}
}
}
// /**
// *
// *
// * @param rowKeysToSort
// * @param cmp
// * @deprecated Use {@link #sortByRows(Collection,Comparator super Object[]>)} instead
// */
// public void sortByRow(Collection rowKeysToSort, Comparator super Object[]> cmp) {
// sortByRows(rowKeysToSort, cmp);
// }
/**
* Sorts the rows in the sheet based on the values in the specified rows using the provided comparator.
* The comparator takes two arrays of objects as input, each array representing a row in the sheet, and returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
*
* @param rowKeysToSort The keys of the rows based on whose values the rows will be sorted.
* @param cmp The comparator to determine the order of the rows. It takes two arrays of objects, each representing a row in the sheet, as input and returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
* @throws IllegalStateException if the sheet is frozen (read-only).
*/
public void sortByRows(final Collection rowKeysToSort, final Comparator super Object[]> cmp) throws IllegalStateException {
checkFrozen();
if (!_isInitialized) {
return;
}
final int sortRowSize = rowKeysToSort.size();
final int[] rowIndexes = new int[sortRowSize];
int idx = 0;
for (final R rowKey : rowKeysToSort) {
rowIndexes[idx++] = getRowIndex(rowKey);
}
final int columnLength = columnLength();
final Indexed