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

Java tutorial

Introduction

Here is the source code for org.omnaest.utils.table.impl.TableDataAccessor.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.io.Serializable;
import java.util.BitSet;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ObjectUtils;
import org.omnaest.utils.events.exception.ExceptionHandler;
import org.omnaest.utils.operation.OperationUtils;
import org.omnaest.utils.operation.special.OperationIntrinsic;
import org.omnaest.utils.operation.special.OperationWithResult;
import org.omnaest.utils.structure.array.ArrayUtils;
import org.omnaest.utils.table.TableEventHandler;

/**
 * Internal data core facade used by the {@link ArrayTable}
 * 
 * @author Omnaest
 * @param <E>
 */
class TableDataAccessor<E> implements Serializable {
    /**
     * The {@link ModificationValidator} returns true for {@link #hasBeenModified()} if there was any write operation to the table
     * data since the {@link ModificationValidator} was created
     * 
     * @author Omnaest
     */
    public class ModificationValidator {
        private long modificationCounterAtCreationTime = TableDataAccessor.this.modificationCounter.get();

        public boolean hasBeenModified() {
            return this.modificationCounterAtCreationTime != TableDataAccessor.this.modificationCounter.get();
        }

        /**
         * This validates and throws a {@link ConcurrentModificationException} if {@link #hasBeenModified()} is true
         * 
         * @throws ConcurrentModificationException
         */
        public void validateForNoModification() throws ConcurrentModificationException {
            if (this.hasBeenModified()) {
                throw new ConcurrentModificationException("Table data has been modified");
            }
        }
    }

    /* ************************************************** Constants *************************************************** */
    private static final long serialVersionUID = -9123078800733926152L;
    /* ************************************** Variables / State (internal/hiding) ************************************* */
    private final AtomicLong modificationCounter = new AtomicLong();

    /* ***************************** Beans / Services / References / Delegates (external) ***************************** */
    private final TableDataCore<E> tableDataCore;
    private final TableEventDispatcher<E> tableEventDispatcher;
    private final ReadWriteLock tableLock = new ReentrantReadWriteLock(true);

    /* ********************************************** Classes/Interfaces ********************************************** */

    private final TableMetaData<E> tableMetaData;

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

    public TableDataAccessor(TableDataCore<E> tableDataCore, TableEventDispatcher<E> tableEventDispatcher,
            TableMetaData<E> tableMetaData) {
        super();
        this.tableDataCore = tableDataCore;
        this.tableEventDispatcher = tableEventDispatcher;
        this.tableMetaData = tableMetaData;

        this.register(tableMetaData);
    }

    public void addColumn(final E... elements) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                int rowIndex = TableDataAccessor.this.tableDataCore.addRow(elements);
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleAddedRow(rowIndex, elements);
            }
        }, this.tableLock.writeLock());
    }

    public void addColumn(final int columnIndex, final E... elements) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                TableDataAccessor.this.tableDataCore.addColumn(columnIndex, elements);
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleAddedColumn(columnIndex, elements);
            }
        }, this.tableLock.writeLock());
    }

    public void addRow(final E[] elements) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                final int rowIndex = TableDataAccessor.this.tableDataCore.addRow(elements);
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleAddedRow(rowIndex, elements);
            }
        }, this.tableLock.writeLock());
    }

    public void addRow(final int rowIndex, final E... elements) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                TableDataAccessor.this.tableDataCore.addRow(rowIndex, elements);
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleAddedRow(rowIndex, elements);
            }
        }, this.tableLock.writeLock());
    }

    public void clear() {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                TableDataAccessor.this.tableDataCore.clear();
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleClearTable();
            }
        }, this.tableLock.writeLock());
    }

    public int columnSize() {
        return OperationUtils.executeWithLocks(new OperationWithResult<Integer>() {
            @Override
            public Integer execute() {
                return TableDataAccessor.this.tableDataCore.columnSize();
            }
        }, this.tableLock.readLock());
    }

    public int getColumnIndex(final Pattern columnTitlePattern) {
        return OperationUtils.executeWithLocks(new OperationWithResult<Integer>() {
            @Override
            public Integer execute() {
                return TableDataAccessor.this.tableMetaData.getColumnIndex(columnTitlePattern);
            }
        }, this.tableLock.readLock());
    }

    public int getColumnIndex(final String columnTitle) {
        return OperationUtils.executeWithLocks(new OperationWithResult<Integer>() {
            @Override
            public Integer execute() {
                return TableDataAccessor.this.tableMetaData.getColumnIndex(columnTitle);
            }
        }, this.tableLock.readLock());
    }

    public BitSet getColumnIndexFilter(final Pattern columnTitlePattern) {
        return OperationUtils.executeWithLocks(new OperationWithResult<BitSet>() {
            @Override
            public BitSet execute() {
                return TableDataAccessor.this.tableMetaData.getColumnIndexFilter(columnTitlePattern);
            }
        }, this.tableLock.readLock());
    }

    public BitSet getColumnIndexFilter(final Set<String> columnTitleSet) {
        return OperationUtils.executeWithLocks(new OperationWithResult<BitSet>() {
            @Override
            public BitSet execute() {
                return TableDataAccessor.this.tableMetaData.getColumnIndexFilter(columnTitleSet);
            }
        }, this.tableLock.readLock());
    }

    public String getColumnTitle(final int columnIndex) {
        return OperationUtils.executeWithLocks(new OperationWithResult<String>() {
            @Override
            public String execute() {
                return TableDataAccessor.this.tableMetaData.getColumnTitle(columnIndex);
            }
        }, this.tableLock.readLock());
    }

    public List<String> getColumnTitleList() {
        return OperationUtils.executeWithLocks(new OperationWithResult<List<String>>() {
            @Override
            public List<String> execute() {
                return TableDataAccessor.this.tableMetaData.getColumnTitleList();
            }
        }, this.tableLock.readLock());
    }

    public E getElement(final int rowIndex, final int columnIndex) {
        return OperationUtils.executeWithLocks(new OperationWithResult<E>() {
            @Override
            public E execute() {
                return TableDataAccessor.this.tableDataCore.getElement(rowIndex, columnIndex);
            }
        }, this.tableLock.readLock());
    }

    public int getRowIndex(final String rowTitle) {
        return OperationUtils.executeWithLocks(new OperationWithResult<Integer>() {
            @Override
            public Integer execute() {
                return TableDataAccessor.this.tableMetaData.getRowIndex(rowTitle);
            }
        }, this.tableLock.readLock());
    }

    public String getRowTitle(final int rowIndex) {
        return OperationUtils.executeWithLocks(new OperationWithResult<String>() {
            @Override
            public String execute() {
                return TableDataAccessor.this.tableMetaData.getRowTitle(rowIndex);
            }
        }, this.tableLock.readLock());
    }

    public List<String> getRowTitleList() {
        return OperationUtils.executeWithLocks(new OperationWithResult<List<String>>() {
            @Override
            public List<String> execute() {
                return TableDataAccessor.this.tableMetaData.getRowTitleList();
            }
        }, this.tableLock.readLock());
    }

    public ReadWriteLock getTableLock() {
        return this.tableLock;
    }

    public String getTableName() {
        return OperationUtils.executeWithLocks(new OperationWithResult<String>() {
            @Override
            public String execute() {
                return TableDataAccessor.this.tableMetaData.getTableName();
            }
        }, this.tableLock.readLock());
    }

    public boolean hasColumnTitles() {
        return OperationUtils.executeWithLocks(new OperationWithResult<Boolean>() {
            @Override
            public Boolean execute() {
                return TableDataAccessor.this.tableMetaData.hasColumnTitles();
            }
        }, this.tableLock.readLock());
    }

    public boolean hasRowTitles() {
        return OperationUtils.executeWithLocks(new OperationWithResult<Boolean>() {
            @Override
            public Boolean execute() {
                return TableDataAccessor.this.tableMetaData.hasRowTitles();
            }
        }, this.tableLock.readLock());
    }

    public boolean hasTableName() {
        return OperationUtils.executeWithLocks(new OperationWithResult<Boolean>() {
            @Override
            public Boolean execute() {
                return TableDataAccessor.this.tableMetaData.hasTableName();
            }
        }, this.tableLock.readLock());
    }

    /**
     * Returns a new {@link ModificationValidator} instance
     * 
     * @return
     */
    public ModificationValidator newModificationValidator() {
        return new ModificationValidator();
    }

    /**
     * Registers a given {@link TableEventHandler} instance. The registration uses weak references, so for any given instance at
     * least one reference must be kept externally
     * 
     * @param tableEventHandler
     * @return the given instance
     */
    public <T extends TableEventHandler<E>> T register(T tableEventHandler) {
        this.tableEventDispatcher.add(tableEventHandler);
        return tableEventHandler;
    }

    /**
     * Unregisters a given {@link TableEventHandler} instance
     * 
     * @param tableEventHandler
     * @return
     */
    public <T extends TableEventHandler<E>> T unregister(T tableEventHandler) {
        this.tableEventDispatcher.remove(tableEventHandler);
        return tableEventHandler;
    }

    public void removeColumn(final int columnIndex) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                final E[] previousElements = TableDataAccessor.this.tableDataCore.removeColumn(columnIndex);
                final String columnTitle = TableDataAccessor.this.getColumnTitle(columnIndex);
                ;
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleRemovedColumn(columnIndex, previousElements,
                        columnTitle);
            }
        }, this.tableLock.writeLock());
    }

    public void removeRow(final int rowIndex) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                final E[] previousElements = TableDataAccessor.this.tableDataCore.removeRow(rowIndex);
                final String rowTitle = TableDataAccessor.this.getRowTitle(rowIndex);
                TableDataAccessor.this.modificationCounter.incrementAndGet();
                TableDataAccessor.this.tableEventDispatcher.handleRemovedRow(rowIndex, previousElements, rowTitle);
            }
        }, this.tableLock.writeLock());

    }

    public int rowSize() {
        return OperationUtils.executeWithLocks(new OperationWithResult<Integer>() {
            @Override
            public Integer execute() {
                return TableDataAccessor.this.tableDataCore.rowSize();
            }
        }, this.tableLock.readLock());
    }

    public void set(final E element, final int rowIndex, final int columnIndex) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                E previousElement = TableDataAccessor.this.tableDataCore.set(element, rowIndex, columnIndex);
                TableDataAccessor.this.modificationCounter.incrementAndGet();

                if (!ObjectUtils.equals(element, previousElement)) {
                    TableDataAccessor.this.tableEventDispatcher.handleUpdatedCell(rowIndex, columnIndex, element,
                            previousElement);
                }
            }
        }, this.tableLock.writeLock());
    }

    public void setColumnTitle(final int columnIndex, final String columnTitle) {
        OperationUtils.executeWithLocks(new OperationWithResult<Void>() {
            @Override
            public Void execute() {
                final String columnTitlePrevious = TableDataAccessor.this.tableMetaData.setColumnTitle(columnIndex,
                        columnTitle);
                TableDataAccessor.this.tableEventDispatcher.handleModifiedColumnTitle(columnIndex, columnTitle,
                        columnTitlePrevious);
                return null;
            }
        }, this.tableLock.writeLock());
    }

    public void setColumnTitles(final Iterable<String> columnTitleIterable) {
        OperationUtils.executeWithLocks(new OperationWithResult<Void>() {
            @Override
            public Void execute() {
                final String[] columnTitles = ArrayUtils.valueOf(columnTitleIterable, String.class);
                final String[] columnTitlesPrevious = TableDataAccessor.this.tableMetaData
                        .setColumnTitles(columnTitles);
                TableDataAccessor.this.tableEventDispatcher.handleModifiedColumnTitles(columnTitles,
                        columnTitlesPrevious);
                return null;
            }
        }, this.tableLock.writeLock());
    }

    public TableDataAccessor<E> setExceptionHandler(ExceptionHandler exceptionHandler) {
        this.tableEventDispatcher.setExceptionHandler(exceptionHandler);
        return this;
    }

    public void setRow(final int rowIndex, final E... elements) {
        OperationUtils.executeWithLocks(new OperationIntrinsic() {
            @Override
            public void execute() {
                E[] previousElements = TableDataAccessor.this.tableDataCore.setRow(rowIndex, elements);
                TableDataAccessor.this.modificationCounter.incrementAndGet();

                final BitSet modifiedIndices = ArrayUtils.differenceBitSet(elements, previousElements);
                if (modifiedIndices.cardinality() > 0) {
                    TableDataAccessor.this.tableEventDispatcher.handleUpdatedRow(rowIndex, elements,
                            previousElements, modifiedIndices);
                }
            }
        }, this.tableLock.writeLock());
    }

    public void setRowTitle(final int rowIndex, final String rowTitle) {
        OperationUtils.executeWithLocks(new OperationWithResult<Void>() {
            @Override
            public Void execute() {
                final String rowTitlePrevious = TableDataAccessor.this.tableMetaData.setRowTitle(rowIndex,
                        rowTitle);
                TableDataAccessor.this.tableEventDispatcher.handleModifiedRowTitle(rowIndex, rowTitle,
                        rowTitlePrevious);
                return null;
            }
        }, this.tableLock.writeLock());
    }

    public void setRowTitles(final Iterable<String> rowTitleIterable) {
        OperationUtils.executeWithLocks(new OperationWithResult<Void>() {
            @Override
            public Void execute() {
                final String[] rowTitles = ArrayUtils.valueOf(rowTitleIterable, String.class);
                final String[] rowTitlesPrevious = TableDataAccessor.this.tableMetaData.setRowTitles(rowTitles);
                TableDataAccessor.this.tableEventDispatcher.handleModifiedRowTitles(rowTitles, rowTitlesPrevious);
                return null;
            }
        }, this.tableLock.writeLock());
    }

    public void setTableName(final String tableName) {
        OperationUtils.executeWithLocks(new OperationWithResult<Void>() {
            @Override
            public Void execute() {
                final String tableNamePrevious = TableDataAccessor.this.tableMetaData.setTableName(tableName);
                TableDataAccessor.this.tableEventDispatcher.handleModifiedTableName(tableName, tableNamePrevious);
                return null;
            }
        }, this.tableLock.writeLock());
    }

}