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

edu.isi.nlp.collections.ImmutableSetMultitable Maven / Gradle / Ivy

The newest version!
package edu.isi.nlp.collections;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import edu.isi.nlp.IsiNlpImmutable;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Nullable;
import org.immutables.value.Value;

/**
 * A Multitable that cannot hold duplicate key-key-value triples. Adding a key-key-value triple that
 * is already in the multitable has no effect.
 *
 * @see Multitable
 * @see ImmutableMultitable
 * @author Chester Palen-Michel, Constantine Lignos, Ryan Gabbard
 */
public final class ImmutableSetMultitable extends ImmutableMultitable
    implements SetMultitable {

  private final ImmutableTable> table;
  private final int size;
  private final ImmutableMap> rowMap;
  private final ImmutableMap> columnMap;
  private final ImmutableSet> cellSet;
  private final ImmutableMultiset allValues;

  private ImmutableSetMultitable(
      final ImmutableTable> table,
      final int size,
      ImmutableSet rowIterationOrder,
      ImmutableSet columnIterationOrder) {

    this.table = checkNotNull(table);
    this.size = size; // all available construction methods ensure size matches table

    // This is more than we generally want in constructor, but we are only caching to return views.
    // cache rowMap
    final ImmutableMap.Builder> rowMapBuilder = ImmutableMap.builder();
    for (final R rowKey : rowIterationOrder) {
      final ImmutableSetMultimap.Builder colMultiBuilder = ImmutableSetMultimap.builder();
      for (final C columnKey : columnIterationOrder) {
        final Collection value = table.get(rowKey, columnKey);
        if (value != null) {
          colMultiBuilder.putAll(columnKey, value);
        }
      }
      rowMapBuilder.put(rowKey, colMultiBuilder.build());
    }
    this.rowMap = rowMapBuilder.build();
    // cache columnMap
    final ImmutableMap.Builder> columnMapBuilder = ImmutableMap.builder();
    for (final C columnKey : columnIterationOrder) {
      final ImmutableSetMultimap.Builder rowMultiBuilder = ImmutableSetMultimap.builder();
      for (final R rowKey : rowIterationOrder) {
        final Collection value = table.get(rowKey, columnKey);
        if (value != null) {
          rowMultiBuilder.putAll(rowKey, value);
        }
      }
      columnMapBuilder.put(columnKey, rowMultiBuilder.build());
    }
    this.columnMap = columnMapBuilder.build();

    // cache cellSet and allValues
    final ImmutableMultiset.Builder valuesBuilder = ImmutableMultiset.builder();
    final ImmutableSet.Builder> cellSetBuilder = ImmutableSet.builder();
    for (final R rowKey : rowIterationOrder) {
      for (final C columnKey : columnIterationOrder) {
        final Collection values = table.get(rowKey, columnKey);
        if (values != null) {
          final ImmutableSetMulticell.Builder multicellBuilder =
              new ImmutableSetMultitable.SetMulticell.Builder()
                  .rowKey(rowKey)
                  .columnKey(columnKey);
          multicellBuilder.values(values);
          valuesBuilder.addAll(values);
          cellSetBuilder.add(multicellBuilder.build());
        }
      }
    }
    this.cellSet = cellSetBuilder.build();
    this.allValues = valuesBuilder.build();
  }

  @Override
  protected Table> table() {
    return table;
  }

  /**
   * Returns the set of values corresponding to the given row and column keys, or an empty set if no
   * such mapping exists. This exists in addition to {@code get} as a type-safe way of returning a
   * set of values at a specified cell.
   *
   * @param rowKey key of row to search for
   * @param columnKey key of column to search for
   */
  @Override
  @SuppressWarnings("unchecked")
  public ImmutableSet getAsSet(@Nullable final Object rowKey, @Nullable final Object columnKey) {
    final Collection ret = table.get(rowKey, columnKey);
    if (ret != null) {
      return (ImmutableSet) ret; // cast guaranteed to succeed because table made of sets
    } else {
      return ImmutableSet.of();
    }
  }

  @Override
  public ImmutableMap> rowMap() {
    return rowMap;
  }

  @Override
  public ImmutableMap> columnMap() {
    return columnMap;
  }

  @Override
  public Set> cellSet() {
    return cellSet;
  }

  /**
   * Returns {@code true} if the table contains a mapping with the specified value. Runs in O(1)
   * time.
   *
   * @param value value to search for
   */
  @Override
  public boolean containsValue(@Nullable final Object value) {
    return allValues.contains(value);
  }

  @Override
  public Collection get(@Nullable final Object rowKey, @Nullable final Object columnKey) {
    final Collection ret = table.get(rowKey, columnKey);
    if (ret != null) {
      return ret;
    } else {
      return ImmutableSet.of();
    }
  }

  @Override
  public ImmutableMultiset values() {
    return allValues;
  }

  @Override
  public int size() {
    return size;
  }

  public static  ImmutableSetMultitable.Builder builder() {
    return new Builder<>();
  }

  public static class Builder implements ImmutableMultitable.Builder {

    // we use the two tables because we both need to maintain insertion order and
    // be able to do lookups during building. The values of both sets are
    // the same identical objects
    private final HashBasedTable> tableWeCanLookUpIn =
        HashBasedTable.create();
    private final ImmutableSet.Builder> rowInsertionOrder =
        ImmutableSet.builder();

    private Builder() {}

    private ImmutableSet.Builder setForKey(final R rowKey, final C columnKey) {
      ImmutableSet.Builder values = tableWeCanLookUpIn.get(rowKey, columnKey);
      if (values == null) {
        values = ImmutableSet.builder();
        tableWeCanLookUpIn.put(rowKey, columnKey, values);
        rowInsertionOrder.add(RowKeyColumnKeyPair.of(rowKey, columnKey));
      }
      return values;
    }

    @Override
    public Builder put(R rowKey, C columnKey, V value) {
      setForKey(rowKey, columnKey).add(value);
      return this;
    }

    @Override
    public Builder putAll(
        final R rowKey, final C columnKey, final Iterable values) {
      setForKey(rowKey, columnKey).addAll(values);
      return this;
    }

    public ImmutableSetMultitable build() {
      final ImmutableTable.Builder> immutableTable = ImmutableTable.builder();

      int size = 0;
      ImmutableSet.Builder rowIterationBuilder = ImmutableSet.builder();
      ImmutableSet.Builder columnIterationBuilder = ImmutableSet.builder();

      for (final RowKeyColumnKeyPair rowKeyColKey : rowInsertionOrder.build()) {
        final ImmutableSet valuesForPair =
            tableWeCanLookUpIn.get(rowKeyColKey.row(), rowKeyColKey.column()).build();
        size += valuesForPair.size();
        immutableTable.put(rowKeyColKey.row(), rowKeyColKey.column(), valuesForPair);
        rowIterationBuilder.add(rowKeyColKey.row());
        columnIterationBuilder.add(rowKeyColKey.column());
      }

      return new ImmutableSetMultitable<>(
          immutableTable.build(),
          size,
          rowIterationBuilder.build(),
          columnIterationBuilder.build());
    }
  }

  @IsiNlpImmutable
  @Value.Immutable
  abstract static class SetMulticell implements SetMultitable.SetMulticell {

    @Override
    public abstract R getRowKey();

    @Override
    public abstract C getColumnKey();

    @Override
    public abstract Set getValues();

    public static class Builder extends ImmutableSetMulticell.Builder {}
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy