/*******************************************************************************
* 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.decorators.datastores;
import gnu.trove.procedure.TIntProcedure;
import gnu.trove.set.hash.TIntHashSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLListener;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
/**
* Decorator which implements table listeners
*
* @author Phil
*
* @param <T>
*/
final public class ListenerDecorator<T extends ODLTableDefinition> extends SimpleDecorator<T> {
/**
*
*/
private static final long serialVersionUID = 663660648531373287L;
private HashMap<ODLListener, TIntHashSet> tableChangedListeners = new HashMap<ODLListener, TIntHashSet>();
private HashSet<ODLListener> tableSetChanged = new HashSet<>();
private boolean enabled = true;
private Pending pending;
private class Pending{
TIntHashSet tablesModified= new TIntHashSet();
boolean tableSetChanged=false;
}
public ListenerDecorator(Class<T> tableClass, ODLDatastore<? extends T> decorated) {
super(tableClass, decorated);
}
@Override
public void addListener(ODLListener tml, int... tableIds) {
switch (tml.getType()) {
case TABLE_CHANGED:
if (tableChangedListeners.containsKey(tml)) {
throw new RuntimeException();
}
tableChangedListeners.put(tml, new TIntHashSet(tableIds));
break;
case DATASTORE_STRUCTURE_CHANGED:
tableSetChanged.add(tml);
break;
}
}
@Override
public void removeListener(ODLListener tml) {
switch (tml.getType()) {
case TABLE_CHANGED:
tableChangedListeners.remove(tml);
break;
case DATASTORE_STRUCTURE_CHANGED:
tableSetChanged.remove(tml);
break;
}
}
private void fireStructureListener(){
if(enabled){
// take copy to stop concurrent modification problems
HashSet<ODLListener> copy = new HashSet<>(tableSetChanged);
for(ODLListener listener : copy){
listener.datastoreStructureChanged();
}
}else{
// save to pending instead
if(pending==null){
pending = new Pending();
}
pending.tableSetChanged = true;
}
}
private void fireTableModelListener(int tableId, int firstRow, int lastRow) {
if (!enabled) {
// save to pending instead
if(pending==null){
pending = new Pending();
}
pending.tablesModified.add(tableId);
return;
}
if (firstRow < 0) {
firstRow = 0;
}
// take copy to stop concurrent modification problems
HashMap<ODLListener,TIntHashSet > copy = new HashMap<>();
copy.putAll(tableChangedListeners);
for (Map.Entry<ODLListener, TIntHashSet> entry : copy.entrySet()) {
if (tableId == -1 || entry.getValue().contains(tableId) || entry.getValue().contains(-1)) {
entry.getKey().tableChanged(-1, firstRow, lastRow);
}
}
}
@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);
}
// Object oldValue = getValueAt(tableId, rowIndex, columnIndex);
//
// super.setValueAt(tableId, aValue, rowIndex, columnIndex);
//
// // Test value has actually changed before firing listener.
// // As type conversion can occur, we should check the value directly from the table
// Object newValue = getValueAt(tableId, rowIndex, columnIndex);
// boolean different = false;
// if(oldValue!=null && newValue==null){
// different = true;
// }
// else if(oldValue==null && newValue!=null){
// different = true;
// }else if(oldValue!=null && newValue!=null && oldValue.equals(newValue)==false){
// different = true;
// }
//
// if(different){
// fireTableModelListener(tableId, rowIndex, rowIndex);
// }
}
// @Override
// public void setRowFlags(int tableId, long flags, long rowId) {
// super.setRowFlags(tableId, flags, rowId);
// fireTableModelListener(tableId, 0, Integer.MAX_VALUE);
// }
@Override
public void setValueById(int tableId, Object aValue, long rowId, int columnIndex) {
Object oldValue = getValueById(tableId, rowId, columnIndex);
super.setValueById(tableId, aValue, rowId, columnIndex);
// Test value has actually changed before firing listener.
// As type conversion can occur, we should check the value directly from the table
Object newValue = getValueById(tableId, rowId, columnIndex);
boolean different = false;
if(oldValue!=null && newValue==null){
different = true;
}
else if(oldValue==null && newValue!=null){
different = true;
}else if(oldValue!=null && newValue!=null && oldValue.equals(newValue)==false){
different = true;
}
if(different){
fireTableModelListener(tableId, 0, Integer.MAX_VALUE);
}
}
@Override
public int createEmptyRow(int tableId, long rowId) {
int ret = super.createEmptyRow(tableId, rowId);
fireTableModelListener(tableId, ret - 1, Integer.MAX_VALUE);
return ret;
}
@Override
public void insertEmptyRow(int tableId, int insertAtRowNb,long rowId) {
super.insertEmptyRow(tableId, insertAtRowNb, rowId);
fireTableModelListener(tableId, insertAtRowNb - 1, Integer.MAX_VALUE);
}
@Override
public void deleteRow(int tableId, int rowNumber) {
super.deleteRow(tableId, rowNumber);
fireTableModelListener(tableId, rowNumber - 1, Integer.MAX_VALUE);
}
@Override
public int addColumn(int tableId, int id,String name, ODLColumnType type, long flags) {
int index =super.addColumn(tableId, id,name, type, flags);
if (index!=-1) {
fireAllListeners(tableId);
return index;
}
return index;
}
@Override
public boolean insertCol(int tableId,int id, int col, String name, ODLColumnType type, long flags, boolean allowDuplicateNames) {
if (super.insertCol(tableId, id,col, name, type, flags, allowDuplicateNames)) {
fireAllListeners(tableId);
return true;
}
return false;
}
@Override
public void deleteCol(int tableId, int col) {
super.deleteCol(tableId, col);
fireAllListeners(tableId);
}
@Override
public T createTable(String tablename, int id) {
T ret = super.createTable(tablename, id);
fireStructureListener();
if (ret != null) {
fireTableModelListener(-1, 0, Integer.MAX_VALUE);
}
return ret;
}
@Override
public void deleteTableById(int tableId) {
super.deleteTableById(tableId);
fireAllListeners(tableId);
}
@Override
public boolean setTableName(int tableId, String newName) {
if(super.setTableName(tableId, newName)){
fireAllListeners(tableId);
return true;
}
return false;
}
@Override
public void setFlags(int tableId, long flags) {
super.setFlags(tableId, flags);
fireAllListeners(tableId);
}
@Override
public void setColumnFlags(int tableId, int col, long flags) {
super.setColumnFlags(tableId, col, flags);
fireAllListeners(tableId);
}
private void fireAllListeners(int tableId) {
fireStructureListener();
fireTableModelListener(tableId, 0, Integer.MAX_VALUE);
}
@Override
public void setColumnDescription(int tableId, int col, String description) {
super.setColumnDescription(tableId, col, description);
fireAllListeners(tableId);
}
@Override
public void setColumnTags(int tableId, int col, Set<String> tags) {
super.setColumnTags(tableId, col, tags);
fireAllListeners(tableId);
}
@Override
public void setTags(int tableId, Set<String> tags) {
super.setTags(tableId, tags);
fireAllListeners(tableId);
}
@Override
public void setColumnDefaultValue(int tableId, int col, Object value) {
super.setColumnDefaultValue(tableId, col, value);
fireAllListeners(tableId);
}
@Override
public void disableListeners() {
enabled = false;
}
@Override
public void enableListeners() {
enabled = true;
// fire anything pending
if(pending!=null){
// fire table set changed listeners first; if tables have been deleted we should close forms first
if(pending.tableSetChanged){
fireStructureListener();
}
pending.tablesModified.forEach(new TIntProcedure() {
@Override
public boolean execute(int tableId) {
fireTableModelListener(tableId, 0, Integer.MAX_VALUE);
return true;
}
});
}
pending = null;
}
}