AbstractGrid.java

/*
 *  Copyright 2014-present Stephen Colebourne
 *
 *  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.joda.collect.grid;

import java.util.AbstractList;
import java.util.List;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;

/**
 * Abstract implementation of the {@code Grid} data structure.
 * 
 * @param <V> the type of the value
 * @author Stephen Colebourne
 */
abstract class AbstractGrid<V> implements Grid<V> {

    /**
     * Validates the row and column counts.
     * 
     * @param rowCount  the row count
     * @param columnCount  the column count
     */
    static void validateCounts(int rowCount, int columnCount) {
        if (rowCount < 0) {
            throw new IllegalArgumentException("Row count must not be negative: " + rowCount + " < 0");
        }
        if (columnCount < 0) {
            throw new IllegalArgumentException("Column count must not be negative: " + columnCount + " < 0");
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Restricted constructor.
     */
    AbstractGrid() {
    }

    //-----------------------------------------------------------------------
    @Override
    public boolean exists(int row, int column) {
        return row >= 0 && row < rowCount() && column >= 0 && column < columnCount();
    }

    //-----------------------------------------------------------------------
    @Override
    public boolean isFull() {
        return size() == rowCount() * columnCount();
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

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

    @Override
    public boolean contains(int row, int column) {
        return cell(row, column) != null;
    }

    @Override
    public boolean containsValue(Object valueToFind) {
        return (valueToFind != null ? values().contains(valueToFind) : false);
    }

    @Override
    public V get(int row, int column) {
        Cell<V> cell = cell(row, column);
        return (cell != null ? cell.getValue() : null);
    }

    @Override
    public Cell<V> cell(int row, int column) {
        if (exists(row, column)) {
            for (Cell<V> cell : cells()) {
                if (cell.getRow() == row && cell.getColumn() == column) {
                    return cell;
                }
            }
        }
        return null;
    }

    //-----------------------------------------------------------------------
    @Override
    public ImmutableCollection<V> values() {
        Builder<V> builder = ImmutableList.builder();
        for (Cell<V> cell : cells()) {
            builder.add(cell.getValue());
        }
        return builder.build();
    }

    //-----------------------------------------------------------------------
    @Override
    public List<V> row(int row) {
        Preconditions.checkElementIndex(row, rowCount(), "Row index");
        return new Inner<V>(this, columnCount(), row, true);
    }

    @Override
    public List<List<V>> rows() {
        return new Outer<V>(this, rowCount(), columnCount(), true);
    }

    @Override
    public List<V> column(int column) {
        Preconditions.checkElementIndex(column, columnCount(), "Column index");
        return new Inner<V>(this, rowCount(), column, false);
    }

    @Override
    public List<List<V>> columns() {
        return new Outer<V>(this, columnCount(), rowCount(), false);
    }

    static class Outer<V> extends AbstractList<List<V>> {
        private final Grid<V> grid;
        private final int size;
        private final int innerSize;
        private final boolean rows;

        Outer(Grid<V> grid, int size, int innerSize, boolean rows) {
            this.grid = grid;
            this.size = size;
            this.innerSize = innerSize;
            this.rows = rows;
        }

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

        @Override
        public List<V> get(int index) {
            Preconditions.checkElementIndex(index, size);
            return new Inner<V>(grid, innerSize, index, rows);
        }
    }

    static class Inner<V> extends AbstractList<V> {
        private final Grid<V> grid;
        private final int size;
        private final int outerIndex;
        private final boolean rows;

        Inner(Grid<V> grid, int size, int outerIndex, boolean rows) {
            this.grid = grid;
            this.size = size;
            this.outerIndex = outerIndex;
            this.rows = rows;
        }

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

        @Override
        public V get(int index) {
            Preconditions.checkElementIndex(index, size);
            if (rows) {
                return grid.get(outerIndex, index);
            } else {
                return grid.get(index, outerIndex);
            }
        }

        @Override
        public V set(int index, V newValue) {
            V old = get(index);
            if (rows) {
                if (newValue == null) {
                    grid.remove(outerIndex, index);
                } else {
                    grid.put(outerIndex, index, newValue);
                }
            } else {
                if (newValue == null) {
                    grid.remove(index, outerIndex);
                } else {
                    grid.put(index, outerIndex, newValue);
                }
            }
            return old;
        }
    }

    //-----------------------------------------------------------------------
    Cell<V> finder(int row, int column) {
        @SuppressWarnings({ "unchecked", "rawtypes" })
        Cell<V> finder = new ImmutableCell(row, column, "");
        return finder;
    }

    //-----------------------------------------------------------------------
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof Grid) {
            Grid<?> other = (Grid<?>) obj;
            return rowCount() == other.rowCount() &&
                    columnCount() == other.columnCount() &&
                    cells().equals(other.cells());
        }
        return false;
    }

    @Override
    public int hashCode() {
        return rowCount() ^ Integer.rotateLeft(columnCount(), 16) ^ cells().hashCode();
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(size() * 16);
        buf.append('[').append(rowCount()).append('x').append(columnCount()).append(':');
        if (size() > 0) {
            for (Cell<V> cell : cells()) {
                buf.append(cell).append(',').append(' ');
            }
            buf.setLength(buf.length() - 2);
        }
        buf.append(']');
        return buf.toString();
    }

}