org.omnaest.utils.table.impl.ArrayTable.java Source code

Java tutorial

Introduction

Here is the source code for org.omnaest.utils.table.impl.ArrayTable.java

Source

/*******************************************************************************
 * Copyright 2012 Danny Kunz
 * 
 * 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.omnaest.utils.table.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.omnaest.utils.assertion.Assert;
import org.omnaest.utils.operation.OperationUtils;
import org.omnaest.utils.operation.special.OperationIntrinsic;
import org.omnaest.utils.structure.array.ArrayUtils;
import org.omnaest.utils.structure.collection.list.ListUtils;
import org.omnaest.utils.structure.collection.set.SetUtils;
import org.omnaest.utils.structure.element.converter.ElementConverter;
import org.omnaest.utils.structure.element.converter.ElementConverterObjectToString;
import org.omnaest.utils.structure.element.converter.ElementConverterSerializable;
import org.omnaest.utils.structure.element.factory.Factory;
import org.omnaest.utils.structure.iterator.IterableUtils;
import org.omnaest.utils.structure.iterator.IteratorUtils;
import org.omnaest.utils.table.Cell;
import org.omnaest.utils.table.Column;
import org.omnaest.utils.table.Columns;
import org.omnaest.utils.table.ImmutableRow;
import org.omnaest.utils.table.ImmutableStripe;
import org.omnaest.utils.table.ImmutableTable;
import org.omnaest.utils.table.Row;
import org.omnaest.utils.table.StripeTransformerPlugin;
import org.omnaest.utils.table.Table;
import org.omnaest.utils.table.TableAdapterManager;
import org.omnaest.utils.table.TableDataSource;
import org.omnaest.utils.table.TableDataSourceCopier;
import org.omnaest.utils.table.TableEventHandler;
import org.omnaest.utils.table.TableExecution;
import org.omnaest.utils.table.TableIndexManager;
import org.omnaest.utils.table.TablePersistenceRegistration;
import org.omnaest.utils.table.TableSelect;
import org.omnaest.utils.table.TableSorter;
import org.omnaest.utils.table.impl.adapter.TableAdapterManagerImpl;
import org.omnaest.utils.table.impl.join.TableSelectImpl;

/**
 * {@link Table} implementation based on an two dimensional array
 * 
 * @author Omnaest
 * @param <E>
 */
public class ArrayTable<E> extends TableAbstract<E> {
    /* ************************************************** Constants *************************************************** */
    private static final long serialVersionUID = 6360131663629436319L;

    /* ************************************** Variables / State (internal/hiding) ************************************* */
    final Class<E> elementType;
    private final TableAdapterManager<E> tableAdapterManager;
    private final TableDataAccessor<E> tableDataAccessor;
    private final TableIndexManager<E, Cell<E>> tableIndexManager;
    private final TablePersistenceRegistration<E> tablePersistenceRegistration;
    private final StripeTransformerPluginManager<E> stripeTransformerPluginManager;

    /* *************************************************** Methods **************************************************** */

    @SuppressWarnings("unchecked")
    public ArrayTable(Class<? extends E> elementType) {
        super();

        Assert.isNotNull(elementType, "The table element type must not be null");

        this.elementType = (Class<E>) elementType;

        final TableMetaData<E> tableMetaData = new TableMetaData<E>();
        final TableDataCore<E> tableDataCore = new TableDataCore<E>(elementType);
        final TableEventDispatcher<E> tableEventDispatcher = new TableEventDispatcher<E>();
        this.tableDataAccessor = new TableDataAccessor<E>(tableDataCore, tableEventDispatcher, tableMetaData)
                .setExceptionHandler(this.exceptionHandler);
        this.tableIndexManager = new TableIndexManagerImpl<E>(this.tableDataAccessor, this, this.exceptionHandler);
        this.tableAdapterManager = new TableAdapterManagerImpl<E>(this, this.exceptionHandler);
        this.tablePersistenceRegistration = this.tableDataAccessor.register(new TablePersistenceRegistrationImpl<E>(
                this, this.tableDataAccessor.getTableLock(), this.exceptionHandler));
        this.stripeTransformerPluginManager = new StripeTransformerPluginManagerImpl<E>();
    }

    @SuppressWarnings("unchecked")
    public ArrayTable(E[][] elementMatrix) {
        this((Class<? extends E>) ArrayUtils.componentType(ArrayUtils.componentType(elementMatrix.getClass())));
        this.copy().from(elementMatrix);
    }

    @Override
    public Table<E> addColumnElements(E... elements) {
        final int columnIndex = this.columnSize();
        return this.addColumnElements(columnIndex, elements);
    }

    @Override
    public Table<E> addColumnElements(int columnIndex, E... elements) {
        this.tableDataAccessor.addColumn(columnIndex, elements);
        return this;
    }

    @Override
    public Table<E> addRowElements(E... elements) {
        this.tableDataAccessor.addRow(elements);
        return this;
    }

    @Override
    public Table<E> addRowElements(int rowIndex, E... elements) {
        this.tableDataAccessor.addRow(rowIndex, elements);
        return this;
    }

    @Override
    public TableAdapterManager<E> as() {
        return this.tableAdapterManager;
    }

    @Override
    public Cell<E> cell(int rowIndex, int columnIndex) {
        return rowIndex >= 0 && columnIndex >= 0
                ? this.tableDataAccessor.register(new CellImpl<E>(rowIndex, columnIndex, this))
                : null;
    }

    @Override
    public Iterable<Cell<E>> cells() {
        return new Iterable<Cell<E>>() {
            @Override
            public Iterator<Cell<E>> iterator() {
                final Iterator<Row<E>> rowIterator = rows().iterator();
                return IteratorUtils.factoryBasedIterator(new Factory<Iterator<Cell<E>>>() {
                    @Override
                    public Iterator<Cell<E>> newInstance() {
                        return rowIterator.hasNext() ? rowIterator.next().cells().iterator() : null;
                    }
                });
            }
        };
    }

    @Override
    public Table<E> clear() {
        this.tableDataAccessor.clear();
        return this;
    }

    @Override
    public Table<E> clone() {
        Table<E> table = new ArrayTable<E>(this.to().array());
        table.setTableName(this.getTableName());
        table.setRowTitles(this.getRowTitleList());
        table.setColumnTitles(this.getColumnTitleList());
        return table;
    }

    @Override
    public Column<E> column(int columnIndex) {
        return columnIndex >= 0 ? this.tableDataAccessor.register(new ColumnImpl<E>(columnIndex, this, false))
                : null;
    }

    @Override
    public Column<E> column(String columnTitle) {
        final int columnIndex = this.tableDataAccessor.getColumnIndex(columnTitle);
        return this.column(columnIndex);
    }

    @Override
    public Columns<E, Column<E>> columns(Pattern columnTitlePattern) {
        final BitSet columnIndexFilter = this.tableDataAccessor.getColumnIndexFilter(columnTitlePattern);
        return new ColumnsImpl<E>(IterableUtils.filtered(this.columns(), columnIndexFilter));
    }

    @Override
    public Columns<E, Column<E>> columns(Set<String> columnTitleSet) {
        final BitSet columnIndexFilter = this.tableDataAccessor.getColumnIndexFilter(columnTitleSet);
        return new ColumnsImpl<E>(IterableUtils.filtered(this.columns(), columnIndexFilter));
    }

    @Override
    public Columns<E, Column<E>> columns(String... columnTitles) {
        return this.columns(SetUtils.valueOf(columnTitles));
    }

    @Override
    public int columnSize() {
        return this.tableDataAccessor.columnSize();
    }

    @Override
    public Class<E> elementType() {
        return this.elementType;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equalsInContent(ImmutableTable<E> table) {
        boolean retval = table != null;

        if (table != null) {
            int rowSize = table.rowSize();
            int columnSize = table.columnSize();

            retval &= this.rowSize() == rowSize;
            retval &= this.columnSize() == columnSize;

            if (retval) {
                Iterator<Row<E>> iteratorRowThis = this.rows().iterator();
                Iterator<ImmutableRow<E>> iteratorRowOther = ((Iterable<ImmutableRow<E>>) table.rows()).iterator();

                while (iteratorRowThis.hasNext() && iteratorRowOther.hasNext()) {
                    ImmutableRow<E> rowThis = iteratorRowThis.next();
                    ImmutableRow<E> rowOther = iteratorRowOther.next();

                    if (!rowThis.equalsInContent(rowOther)) {
                        retval = false;
                        break;
                    }
                }

                retval &= !iteratorRowThis.hasNext() && !iteratorRowOther.hasNext();
            }
        }

        return retval;
    }

    @Override
    public boolean equalsInContentAndMetaData(ImmutableTable<E> table) {
        final boolean equalsInContent = this.equalsInContent(table);

        final boolean equalsInMetaData = table != null
                && StringUtils.equals(this.getTableName(), table.getTableName())
                && ObjectUtils.equals(this.getRowTitleList(), table.getRowTitleList())
                && ObjectUtils.equals(this.getColumnTitleList(), table.getColumnTitleList());

        return equalsInContent && equalsInMetaData;
    }

    @Override
    public ImmutableTable<E> executeWithReadLock(TableExecution<ImmutableTable<E>, E> tableExecution) {
        OperationUtils.executeWithLocks(tableExecution, this, this.tableDataAccessor.getTableLock().readLock());
        return this;
    }

    @Override
    public Table<E> executeWithReadLock(final TableExecution<ImmutableTable<E>, E> tableExecution,
            final ImmutableTable<E>... furtherLockedTables) {
        final int furtherLockedTablesLength = furtherLockedTables.length;
        if (furtherLockedTablesLength > 0) {
            OperationUtils.executeWithLocks(new OperationIntrinsic() {
                @Override
                public void execute() {
                    OperationUtils.executeWithLocks(tableExecution, ArrayTable.this,
                            ArrayTable.this.tableDataAccessor.getTableLock().readLock());
                    final ImmutableTable<E> furtherTable = furtherLockedTables[0];
                    furtherTable.executeWithReadLock(tableExecution,
                            Arrays.copyOfRange(furtherLockedTables, 1, furtherLockedTablesLength));
                }
            }, this.tableDataAccessor.getTableLock().readLock());
        } else {
            OperationUtils.executeWithLocks(tableExecution, this, this.tableDataAccessor.getTableLock().readLock());
        }
        return this;
    }

    @Override
    public Table<E> executeWithWriteLock(TableExecution<Table<E>, E> tableExecution) {
        OperationUtils.executeWithLocks(tableExecution, this, this.tableDataAccessor.getTableLock().writeLock());
        return this;
    }

    @Override
    public E getElement(int rowIndex, int columnIndex) {
        return this.tableDataAccessor.getElement(rowIndex, columnIndex);
    }

    @Override
    public E getElement(String rowTitle, int columnIndex) {
        final int rowIndex = this.tableDataAccessor.getRowIndex(rowTitle);
        return this.tableDataAccessor.getElement(rowIndex, columnIndex);
    }

    @Override
    public E getElement(int rowIndex, String columnTitle) {
        final int columnIndex = this.tableDataAccessor.getColumnIndex(columnTitle);
        return this.tableDataAccessor.getElement(rowIndex, columnIndex);
    }

    @Override
    public E getElement(String rowTitle, String columnTitle) {
        final int rowIndex = this.tableDataAccessor.getRowIndex(rowTitle);
        final int columnIndex = this.tableDataAccessor.getColumnIndex(columnTitle);
        return this.tableDataAccessor.getElement(rowIndex, columnIndex);
    }

    @Override
    public String getColumnTitle(int columnIndex) {
        return this.tableDataAccessor.getColumnTitle(columnIndex);
    }

    @Override
    public List<String> getColumnTitleList() {
        return this.tableDataAccessor.getColumnTitleList();
    }

    @Override
    public String getRowTitle(int rowIndex) {
        return this.tableDataAccessor.getRowTitle(rowIndex);
    }

    @Override
    public List<String> getRowTitleList() {
        return this.tableDataAccessor.getRowTitleList();
    }

    @Override
    public String getTableName() {
        return this.tableDataAccessor.getTableName();
    }

    @Override
    public boolean hasColumnTitles() {
        return this.tableDataAccessor.hasColumnTitles();
    }

    @Override
    public boolean hasRowTitles() {
        return this.tableDataAccessor.hasRowTitles();
    }

    @Override
    public boolean hasTableName() {
        return this.tableDataAccessor.hasTableName();
    }

    @Override
    public TableIndexManager<E, Cell<E>> index() {
        return this.tableIndexManager;
    }

    @Override
    public TablePersistenceRegistration<E> persistence() {
        return this.tablePersistenceRegistration;
    }

    @Override
    public Table<E> removeColumn(int columnIndex) {
        this.tableDataAccessor.removeColumn(columnIndex);
        return this;
    }

    @Override
    public Table<E> removeRow(int rowIndex) {
        this.tableDataAccessor.removeRow(rowIndex);
        return this;
    }

    @Override
    public Row<E> row(int rowIndex) {
        return rowIndex >= 0 ? this.tableDataAccessor.register(new RowImpl<E>(rowIndex, this, false)) : null;
    }

    @Override
    public Row<E> row(String rowTitle) {
        final int rowIndex = this.tableDataAccessor.getRowIndex(rowTitle);
        return this.row(rowIndex);
    }

    @Override
    public int rowSize() {
        return this.tableDataAccessor.rowSize();
    }

    @Override
    public TableSelect<E> select() {
        return new TableSelectImpl<E>(this);
    }

    @Override
    public Table<E> setColumnTitle(int columnIndex, String columnTitle) {
        this.tableDataAccessor.setColumnTitle(columnIndex, columnTitle);
        return this;
    }

    @Override
    public Table<E> setColumnTitles(Iterable<String> columnTitleIterable) {
        this.tableDataAccessor.setColumnTitles(columnTitleIterable);
        return this;
    }

    @Override
    public Table<E> setElement(int rowIndex, int columnIndex, E element) {
        this.tableDataAccessor.set(element, rowIndex, columnIndex);
        return this;
    }

    @Override
    public Table<E> setElement(int rowIndex, String columnTitle, E element) {
        final int columnIndex = this.tableDataAccessor.getColumnIndex(columnTitle);
        this.setElement(rowIndex, columnIndex, element);
        return this;
    }

    @Override
    public Table<E> setElement(String rowTitle, int columnIndex, E element) {
        final int rowIndex = this.tableDataAccessor.getRowIndex(rowTitle);
        this.setElement(rowIndex, columnIndex, element);
        return this;
    }

    @Override
    public Table<E> setElement(String rowTitle, String columnTitle, E element) {
        final int columnIndex = this.tableDataAccessor.getColumnIndex(columnTitle);
        final int rowIndex = this.tableDataAccessor.getRowIndex(rowTitle);
        this.setElement(rowIndex, columnIndex, element);
        return this;
    }

    @Override
    public Table<E> setRowElements(int rowIndex, E... elements) {
        this.tableDataAccessor.setRow(rowIndex, elements);
        return this;
    }

    @Override
    public Table<E> setRowTitle(int rowIndex, String rowTitle) {
        this.tableDataAccessor.setRowTitle(rowIndex, rowTitle);
        return this;
    }

    @Override
    public Table<E> setRowTitles(Iterable<String> rowTitleIterable) {
        this.tableDataAccessor.setRowTitles(rowTitleIterable);
        return this;
    }

    @Override
    public Table<E> setTableName(String tableName) {
        this.tableDataAccessor.setTableName(tableName);
        return this;
    }

    @Override
    public TableSorter<E> sort() {
        return new TableSorterImpl<E>(this);
    }

    @Override
    public int getColumnIndex(String columnTitle) {
        return this.tableDataAccessor.getColumnIndex(columnTitle);
    }

    @Override
    public Table<E> register(StripeTransformerPlugin<E, ?> stripeTransformerPlugin) {
        this.stripeTransformerPluginManager.register(stripeTransformerPlugin);
        return this;
    }

    @Override
    public <T> T transformStripeInto(Class<T> type, ImmutableStripe<E> stripe) {
        T retval = null;
        if (stripe != null) {
            final StripeTransformerPlugin<E, T> stripeTransformerPlugin = this.stripeTransformerPluginManager
                    .resolveStripeTransformerPluginFor(type);
            if (stripeTransformerPlugin != null) {
                try {
                    retval = stripeTransformerPlugin.transform(stripe);
                } catch (Exception e) {
                    this.exceptionHandler.handleException(e);
                }
            }
        }
        return retval;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T transformStripeInto(T instance, ImmutableStripe<E> stripe) {
        T retval = null;
        if (instance != null && stripe != null) {
            final Class<T> type = (Class<T>) instance.getClass();
            final StripeTransformerPlugin<E, T> stripeTransformerPlugin = this.stripeTransformerPluginManager
                    .resolveStripeTransformerPluginFor(type);
            if (stripeTransformerPlugin != null) {
                try {
                    retval = stripeTransformerPlugin.transform(stripe, instance);
                } catch (Exception e) {
                    this.exceptionHandler.handleException(e);
                }
            }
        }
        return retval;
    }

    @Override
    public TableDataSourceCopier<E> copy() {
        final Table<E> table = this;
        return new TableDataSourceCopier<E>() {
            private static final long serialVersionUID = 306474856413841605L;

            @Override
            public Table<E> from(E[][] elementMatrix) {
                if (elementMatrix != null) {
                    for (E[] elements : elementMatrix) {
                        table.addRowElements(elements);
                    }
                }
                return table;
            }

            @Override
            public Table<E> from(TableDataSource<E> tableDataSource) {
                if (tableDataSource != null) {
                    final Iterable<E[]> rowElements = tableDataSource.rowElements();
                    if (rowElements != null) {
                        for (E[] elements : rowElements) {
                            table.addRowElements(elements);
                        }
                    }

                    final String[] columnTitles = tableDataSource.getColumnTitles();
                    final String[] rowTitles = tableDataSource.getRowTitles();
                    final String tableName = tableDataSource.getTableName();
                    if (tableName != null) {
                        table.setTableName(tableName);
                    }
                    if (columnTitles != null) {
                        table.setColumnTitles(columnTitles);
                    }
                    if (rowTitles != null) {
                        table.setRowTitles(rowTitles);
                    }
                }
                return table;
            }
        };
    }

    @Override
    public String[] getColumnTitles() {
        return ArrayUtils.valueOf(this.getColumnTitleList(), String.class);
    }

    @Override
    public TableEventHandlerRegistration<E, Table<E>> tableEventHandlerRegistration() {
        final Table<E> table = this;
        final TableDataAccessor<E> tableDataAccessor = this.tableDataAccessor;
        return new TableEventHandlerRegistration<E, Table<E>>() {
            private static final long serialVersionUID = -4733568643076274493L;

            @Override
            public Table<E> attach(TableEventHandler<E> tableEventHandler) {
                tableDataAccessor.register(tableEventHandler);
                return table;
            }

            @Override
            public Table<E> detach(TableEventHandler<E> tableEventHandler) {
                tableDataAccessor.unregister(tableEventHandler);
                return table;
            }
        };
    }

    @Override
    public Iterable<E[]> rowElements() {
        final ElementConverter<Row<E>, E[]> elementConverter = new ElementConverterSerializable<Row<E>, E[]>() {
            private static final long serialVersionUID = -4211554274134868391L;

            @Override
            public E[] convert(Row<E> row) {
                return row.to().array();
            }
        };
        return IterableUtils.adapter(this.rows(), elementConverter);
    }

    @Override
    public Row<E> row(int rowIndex, boolean detached) {
        return rowIndex >= 0 ? new RowImpl<E>(rowIndex, this, detached) : null;
    }

    @Override
    public Table<E> setColumnTitlesUsingFirstRow() {
        final Row<E> row = this.row(0);
        final E[] elements = row.to().array();
        final String[] columnTitles = ArrayUtils.convertArray(elements, String.class,
                new ElementConverterObjectToString());
        this.setColumnTitles(columnTitles);
        row.remove();
        return this;
    }

    @Override
    public Table<E> setRowTitlesUsingFirstColumn() {
        final Column<E> column = this.column(0);
        final E[] elements = column.to().array();
        final String[] rowTitles = ArrayUtils.convertArray(elements, String.class,
                new ElementConverterObjectToString());
        this.setRowTitles(rowTitles);
        column.remove();
        return this;
    }

    @Override
    public Table<E> addRowElements(Map<String, E> columnToElementMap, boolean createColumnTitleIfDontExists) {
        if (columnToElementMap != null) {
            final List<E> elementList = new ArrayList<E>();
            {
                final Set<String> columnTitleSet = columnToElementMap.keySet();
                final List<String> columnTitleList = new ArrayList<String>(this.getColumnTitleList());
                for (String columnTitle : columnTitleSet) {
                    int indexOf = columnTitleList.indexOf(columnTitle);
                    if (createColumnTitleIfDontExists && indexOf < 0) {
                        indexOf = columnTitleList.size();
                        columnTitleList.add(columnTitle);
                        this.addColumnTitle(columnTitle);
                    }
                    if (indexOf >= 0) {
                        final E element = columnToElementMap.get(columnTitle);
                        ListUtils.set(elementList, indexOf, element);
                    }
                }
            }
            this.addRowElements(elementList);
        }
        return this;
    }

    @Override
    public Table<E> addRowElements(Iterable<E> elementIterable) {
        if (elementIterable != null) {
            final E[] elements = ArrayUtils.valueOf(elementIterable, this.elementType);
            this.addRowElements(elements);
        }
        return this;
    }
}