/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.core.tables.concurrency;
import gnu.trove.map.hash.TLongObjectHashMap;
import gnu.trove.set.hash.TIntHashSet;
import gnu.trove.set.hash.TLongHashSet;
import java.util.BitSet;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLTable;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.api.tables.ODLTableReadOnly;
import com.opendoorlogistics.core.tables.decorators.datastores.SimpleDecorator;
/**
* A decorator which records all writes (row creation, insertion, deletion, setting
* values) and does not allow any structure modification with the exception of tables
* created via this decorator.
* UnsupportedOperationException are thrown if the client code tries to modify table structure
* for any tables not created via this decorator.
* @author Phil
*
* @param <T>
*/
final public class WriteRecorderDecorator<T extends ODLTableDefinition> extends SimpleDecorator<T>{
private final TLongHashSet deletedOriginalRowIds = new TLongHashSet();
private final TLongHashSet appendedRowIds = new TLongHashSet();
private final TLongObjectHashMap<BitSet> setCols = new TLongObjectHashMap<>();
private final TIntHashSet createdTableIds = new TIntHashSet();
public WriteRecorderDecorator(Class<T> tableClass, ODLDatastore<? extends T> decorated) {
super(tableClass, decorated);
}
public long [] getWrittenRowIds(){
return setCols.keys();
}
public BitSet getWrittenCols(long rowId){
return setCols.get(rowId);
}
public long [] getDeletedOriginalRowIds(){
return deletedOriginalRowIds.toArray();
}
public boolean isCreatedTable(int tableId){
return createdTableIds.contains(tableId);
}
public boolean isAppendedRow(long globalRowId){
return appendedRowIds.contains(globalRowId);
}
@Override
public void setValueById(int tableId, Object aValue, long rowId, int columnIndex) {
// doCommand(new SetByRowId(tableId, rowId, columnIndex, aValue));
super.setValueById(tableId, aValue, rowId, columnIndex);
ODLTableReadOnly table = readOnlyTable(tableId);
if(table!=null){
BitSet bs = setCols.get(rowId);
if(bs == null){
bs = new BitSet(table.getColumnCount());
setCols.put(rowId, bs);
}
bs.set(columnIndex);
}
}
@Override
public void setValueAt(int tableId, Object aValue, int rowIndex, int columnIndex) {
long id = getRowGlobalId(tableId, rowIndex);
if(id!=-1){
setValueById(tableId, aValue, id, columnIndex);
}
}
@Override
public int createEmptyRow(int tableId, long rowId) {
int indx= super.createEmptyRow(tableId, rowId);
appendedRowIds.add(getRowGlobalId(tableId, indx));
return indx;
}
@Override
public void insertEmptyRow(int tableId, int insertAtRowNb, long rowId) {
// only allow insertion at end
ODLTable table = (ODLTable)getTableByImmutableId(tableId);
if(insertAtRowNb == table.getRowCount()){
int insertedAt = createEmptyRow(tableId, rowId);
if(insertedAt!=insertAtRowNb){
throw new RuntimeException();
}
}else{
// running scripts which insert rows will screw up the merger with the original datastore...
throw new UnsupportedOperationException();
}
}
@Override
public void deleteRow(int tableId, int rowNumber) {
long rowId = getRowGlobalId(tableId, rowNumber);
if(appendedRowIds.contains(rowId)==false){
deletedOriginalRowIds.add(rowId);
}
super.deleteRow(tableId, rowNumber);
}
@Override
public int addColumn(int tableId,int id, String name, ODLColumnType type, long flags) {
if(createdTableIds.contains(tableId)){
return super.addColumn(tableId, id,name, type, flags);
}
throw new UnsupportedOperationException();
}
@Override
public void setFlags(int tableId, long flags) {
if(createdTableIds.contains(tableId)){
super.setFlags(tableId, flags);
return;
}
throw new UnsupportedOperationException();
}
@Override
public void setColumnFlags(int tableId, int col, long flags) {
if(createdTableIds.contains(tableId)){
super.setColumnFlags(tableId, col, flags);
return;
}
throw new UnsupportedOperationException();
}
@Override
public void deleteCol(int tableId, int col) {
if(createdTableIds.contains(tableId)){
super.deleteCol(tableId, col);
return;
}
throw new UnsupportedOperationException();
}
@Override
public boolean insertCol(int tableId,int id, int col, String name, ODLColumnType type, long flags, boolean allowDuplicateNames) {
if(createdTableIds.contains(tableId)){
return super.insertCol(tableId, id,col, name, type, flags, allowDuplicateNames);
}
throw new UnsupportedOperationException();
}
@Override
public T createTable(String tablename, int tableId) {
T ret = super.createTable(tablename, tableId);
createdTableIds.add(ret.getImmutableId());
return ret;
}
@Override
public void deleteTableById(int tableId) {
throw new UnsupportedOperationException();
}
@Override
public boolean setTableName(int tableId, String newName) {
if(createdTableIds.contains(tableId)){
return super.setTableName(tableId, newName);
}
throw new UnsupportedOperationException();
}
@Override
public void setFlags(long flags) {
throw new UnsupportedOperationException();
}
}