All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cp.elements.data.struct.tabular.View Maven / Gradle / Ivy

/*
 * Copyright 2011-Present Author or Authors.
 *
 * 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 org.cp.elements.data.struct.tabular;

import static org.cp.elements.lang.RuntimeExceptionsFactory.newIndexOutOfBoundsException;

import java.util.Comparator;
import java.util.Optional;
import java.util.function.Predicate;

import org.cp.elements.data.struct.tabular.query.Query;
import org.cp.elements.function.FunctionUtils;
import org.cp.elements.lang.Assert;
import org.cp.elements.lang.Integers;
import org.cp.elements.lang.Nameable;
import org.cp.elements.lang.StringUtils;
import org.cp.elements.lang.annotation.NotNull;
import org.cp.elements.lang.annotation.NullSafe;
import org.cp.elements.lang.annotation.Nullable;
import org.cp.elements.util.stream.StreamUtils;

/**
 * Abstract Data Type (ADT) defining a limited view, projection, or image of a tabular data structure.
 *
 * @author John Blum
 * @see java.lang.Iterable
 * @see java.util.function.Predicate
 * @see org.cp.elements.data.struct.tabular.AbstractView
 * @see org.cp.elements.data.struct.tabular.Column
 * @see org.cp.elements.data.struct.tabular.Row
 * @see org.cp.elements.data.struct.tabular.Table
 * @see org.cp.elements.data.struct.tabular.query.Query
 * @see org.cp.elements.lang.Nameable
 * @since 1.0.0
 */
public interface View extends Iterable, Nameable {

  /**
   * Returns an {@link Iterable} iterating over the {@link Column Columns} in this {@link View}.
   *
   * @return an {@link Iterable} iterating over the {@link Column Columns} in this {@link View}.
   * @see org.cp.elements.data.struct.tabular.Column
   * @see java.lang.Iterable
   * @see #rows()
   */
  Iterable> columns();

  /**
   * Determines whether this {@link View} contains the given {@link Column}.
   *
   * @param column {@link Column} being evaluated.
   * @return a boolean value indicating whether this {@link View} contains the given {@link Column}.
   * @see org.cp.elements.data.struct.tabular.Column
   * @see #contains(String)
   */
  @NullSafe
  default boolean contains(@NotNull Column column) {
    return column != null && contains(column.getName());
  }

  /**
   * Determines whether this {@link View} contains a {@link Column} with the given {@link String name}.
   *
   * @param columnName {@link String} containing the {@literal name} of the {@link Column} to evaluate.
   * @return a boolean value indicating whether this {@link View} contains a {@link Column}
   * with the given {@link String name}.
   * @see #columns()
   */
  @NullSafe
  default boolean contains(@NotNull String columnName) {

    return StringUtils.hasText(columnName)
      && StreamUtils.stream(columns()).anyMatch(column -> column.getName().equals(columnName));
  }

  /**
   * Counts the {@link Row Rows} in this {@link View} that match the given, required {@link Predicate}.
   *
   * @param predicate {@link Predicate} used to match {@link Row Rows} included in the {@link Integer count};
   * must not be {@literal null}.
   * @return a {@link Integer count} of the number of {@link Row Rows} matching the given {@link Predicate}.
   * @throws IllegalArgumentException if the given {@link Predicate} is {@literal null}.
   * @see org.cp.elements.data.struct.tabular.Row
   * @see java.util.function.Predicate
   */
  default int count(@NotNull Predicate predicate) {

    Assert.notNull(predicate, "Predicate is required");

    long count = StreamUtils.stream(rows())
      .filter(predicate)
      .count();

    return Long.valueOf(count).intValue();
  }

  /**
   * Returns the {@link Column} in this {@link View} at the given {@link Integer index}.
   *
   * @param  {@link Class type} of {@link Object values} stored in the {@link Column} of this {@link View}.
   * @param index {@link Integer} declaring the {@literal index} of the {@link Column} to return from this {@link View}.
   * @return the {@link Column} in this {@link View} at the given {@link Integer index}.
   * @throws IllegalArgumentException if the given {@link Integer index} is less than {@literal 0}.
   * @throws IndexOutOfBoundsException if the given {@link Integer index} is greater than
   * the number of {@link Column Columns} in this {@link View}.
   * @see org.cp.elements.data.struct.tabular.Column
   * @see #columns()
   */
  @SuppressWarnings("unchecked")
  default @NotNull  Column getColumn(int index) {

    Assert.isTrue(index > Integers.MINUS_ONE, () -> String.format("Column index [%d] is not valid", index));

    int count = 0;

    for (Column column : columns()) {
      if (count++ == index) {
        return (Column) column;
      }
    }

    throw newIndexOutOfBoundsException("Index [%1$d] is greater than the number of columns [%2$d] in this View [%3$s]",
      index, count, getName());
  }

  /**
   * Optionally returns a {@link Column} with the given {@link String name} from this {@link View}.
   *
   * @param  {@link Class type} of {@link Object values} stored in the {@link Column} of this {@link View}.
   * @param name {@link String} containing the {@link String name} of the {@link Column} to return
   * from this {@link View}.
   * @return an {@link Optional} {@link Column} from this {@link View} with the given {@link String name}
   * or {@link Optional#empty()} if no {@link Column} in this {@link View} with the given {@link String name} exists.
   * @see org.cp.elements.data.struct.tabular.Column
   * @see java.util.Optional
   * @see #columns()
   */
  @SuppressWarnings("unchecked")
  default  Optional> getColumn(@NotNull String name) {

    return StreamUtils.stream(columns())
      .filter(column -> column.getName().equals(name))
      .map(column -> (Column) column)
      .findFirst();
  }

  /**
   * Returns the {@link Row} in this {@link View} at the given {@link Integer index}.
   *
   * @param index {@link Integer} declaring the {@literal index} of the {@link Row} to return from this {@link View}.
   * @return the {@link Row} in this {@link View} at the given {@link Integer index}.
   * @throws IllegalArgumentException if the given {@link Integer index} is less than {@literal 0}.
   * @throws IndexOutOfBoundsException if given {@link Integer index} is greater than the number of {@link Row Rows}
   * in this {@link View}.
   * @see org.cp.elements.data.struct.tabular.Row
   * @see #rows()
   * @see #size()
   */
  default @NotNull Row getRow(int index) {

    int size = size();

    Assert.isTrue(index > Integers.MINUS_ONE && index < size,
      () -> String.format("Row index [%1$d] is not valid; Row index must be greater than [-1] and less than [%2$d]",
        index, size));

    int count = 0;

    for (Row row : rows()) {
      if (count++ == index) {
        return row;
      }
    }

    throw newIndexOutOfBoundsException("Row index [%1$d] is greater than the number of rows [%2$d] in this View [%3$s]",
      index, count, getName());
  }

  /**
   * Optionally return the first {@link Row} in this {@link View} matching the given, required {@link Predicate}.
   *
   * @param predicate {@link Predicate} defining criteria used to match the {@link Row}; must not be {@literal null}.
   * @return the first {@link Row} in this {@link View} matching the given {@link Predicate}
   * or {@link Optional#empty()} if no {@link Row} in this {@link View} matches the given {@link Predicate}
   * or {@link Optional#empty()} if the given {@link Predicate} is {@literal null}.
   * @see org.cp.elements.data.struct.tabular.Row
   * @see java.util.function.Predicate
   * @see java.util.Optional
   * @see #rows()
   */
  default Optional getRow(@NotNull Predicate predicate) {

    Predicate resolvedPredicate = FunctionUtils.nullSafePredicateMatchNone(predicate);

    return StreamUtils.stream(rows())
      .filter(resolvedPredicate)
      .findFirst();
  }

  /**
   * Returns the {@link Object value} in this {@link View} at the given {@link Integer row index}
   * and {@link Integer column index}.
   *
   * @param  {@link Class type} of the {@link Object value}.
   * @param rowIndex {@link Integer} declaring the {@literal index} of a {@link Row} in this {@link View}.
   * @param columnIndex {@link Integer} declaring the {@literal index} of a {@link Column} in this {@link View}.
   * @return the {@link Object value} in this {@link View} at the given {@link Integer row index}
   * and {@link Integer column index}.
   * @throws IllegalArgumentException if the given {@link Integer row index} or {@link Integer column index}
   * are less than {@literal 0}.
   * @throws IndexOutOfBoundsException if the {@link Integer row index} or {@link Integer column index}
   * are not valid indexes in this tabular {@link View}.
   * @see org.cp.elements.data.struct.tabular.Row#getValue(int)
   * @see #getRow(int)
   */
  default @Nullable  T getValue(int rowIndex, int columnIndex) {
    return getRow(rowIndex).getValue(columnIndex);
  }

  /**
   * Returns the {@link Object value} in this {@link View} at the given {@link Integer row index}
   * and {@link Column} with the given {@link String name}.
   *
   * @param  {@link Class type} of the {@link Object value}.
   * @param rowIndex {@link Integer} declaring the {@literal index} of a {@link Row} in this {@link View}.
   * @param columnName {@link String} containing the {@literal name} of the {@link Column} from this {@link View}
   * in which the {@link Object value} will be returned.
   * @return the {@link Object value} in this {@link View} at the given {@link Integer row index}
   * and {@link Column} with the given {@link String name}.
   * @throws IllegalArgumentException if the given {@link Integer row index} is less than {@literal 0}
   * or a {@link Column} with {@link String name} does not exist in this {@link View}.
   * @throws IndexOutOfBoundsException if the {@link Integer row index} is not valid index
   * in this tabular {@link View}.
   * @see #getValue(int, int)
   * @see #indexOf(String)
   */
  default @Nullable  T getValue(int rowIndex, @NotNull String columnName) {

    int columnIndex = indexOf(columnName);

    Assert.isTrue(columnIndex > Integers.MINUS_ONE,
      () -> String.format("Column with name [%1$s] does not exist in this View [%2$s]", columnName, getName()));

    return getValue(rowIndex, columnIndex);
  }

  /**
   * Returns the {@link Object value} in this {@link View} at the given {@link Integer row index} and {@link Column}.
   *
   * @param  {@link Class type} of the {@link Object value}.
   * @param rowIndex {@link Integer} declaring the {@literal index} of a {@link Row} in this {@link View}.
   * @param column {@link Column} in this {@link View} from which to get the {@link Object value}.
   * @return the {@link Object value} in this {@link View} at the given {@link Integer row index} and {@link Column}.
   * @throws IllegalArgumentException if the given {@link Integer row index} is less than {@literal 0}
   * or the given {@link Column} does not exist in this {@link View}.
   * @throws IndexOutOfBoundsException if the {@link Integer row index} is not a valid index
   * in this tabular {@link View}.
   * @see org.cp.elements.data.struct.tabular.Column
   * @see #getValue(int, int)
   * @see #indexOf(Column)
   */
  default @Nullable  T getValue(int rowIndex, @NotNull Column column) {

    int columnIndex = indexOf(column);

    Assert.isTrue(columnIndex > Integers.MINUS_ONE,
      () -> String.format("Column [%1$s] does not exist in this View [%2$s]", column, getName()));

    return getValue(rowIndex, columnIndex);
  }

  /**
   * Determines the {@link Integer index} of the given {@link Column} in this {@link View} if present.
   * 

* The first {@link Column} is at {@link Integer index} {@literal 0}. * * @param column {@link Column} to evaluate. * @return the {@link Integer index} of the given {@link Column} in this {@link View}, * or {@literal -1} if the {@link Column} is not contained in this {@link View}. * @see org.cp.elements.data.struct.tabular.Column * @see #indexOf(String) */ @NullSafe default int indexOf(@NotNull Column column) { return column != null ? indexOf(column.getName()) : Integers.MINUS_ONE; } /** * Determines the {@link Integer index} of a {@link Column} with the given {@link String name} in this {@link View} * if present. *

* The first {@link Column} is at {@link Integer index} {@literal 0}. * * @param columnName {@link String} containing the {@literal name} of the {@link Column} to index. * @return the {@link Integer index} of a {@link Column} with the given {@link String name} in this {@link View}, * or {@literal -1} if a {@link Column} with {@link String name} is not contained in this {@link View}. * @see #columns() */ @NullSafe default int indexOf(@NotNull String columnName) { int index = 0; for (Column column : columns()) { if (column.getName().equals(columnName)) { return index; } index++; } return Integers.MINUS_ONE; } /** * Determines the {@link Integer index} of the given {@link Row} in this {@link View} if present. * * @param row {@link Row} to evaluate. * @return the {@link Integer index} of the given {@link Row} in this {@link View}, * or {@literal -1} if the {@link Row} is not contained in this {@link View}. * @see org.cp.elements.data.struct.tabular.Row * @see #rows() */ @NullSafe default int indexOf(@NotNull Row row) { int index = 0; for (Row viewRow : rows()) { if (viewRow.equals(row)) { return index; } index++; } return Integers.MINUS_ONE; } /** * Determines whether this {@link View} is {@literal empty}, or whether this {@link View} * contains any {@link Row Rows}. *

* By default, this {@link View} is considered {@literal empty} if {@link #size()} is {@literal 0}. * * @return a boolean value indicating whether this {@link View} contains any {@link Row Rows}. * @see #size() */ default boolean isEmpty() { return size() < Integers.ONE; } /** * Queries this {@link View} with the given {@link Query} and returns a new {@link View} from the result set. *

* The {@link Query} matches {@link Row Rows}, or {@literal data} contained in this {@link View} * defined by a {@link Predicate}, ordered/sorted by a {@link Comparator} and projected using an array * or {@link Iterable} of {@link Column Columns}. * * @param query {@link Query} object defining the {@literal query} on this {@link View}; * must not be {@literal null}. * @return a new {@link View} from the result set produced by the given {@link Query}. * @throws IllegalArgumentException if the given {@link Query} is {@literal null}. * @see org.cp.elements.data.struct.tabular.query.Query * @see org.cp.elements.data.struct.tabular.View */ default @NotNull View query(@NotNull Query query) { Assert.notNull(query, "Query is required"); return query.from(this).execute(); } /** * Returns an {@link Iterable} iterating over the {@link Row Rows} in this {@link View}. * * @return an {@link Iterable} iterating over the {@link Row Rows} in this {@link View}. * @see org.cp.elements.data.struct.tabular.Row * @see java.lang.Iterable * @see #columns() */ default Iterable rows() { return this; } /** * Returns the {@link Integer number} of {@link Row Rows} in this {@link View}. * * @return the {@link Integer number} of {@link Row Rows} in this {@link View}. * @see #count(Predicate) */ default int size() { return count(row -> true); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy