/******************************************************************************* * 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.memory; import gnu.trove.list.array.TLongArrayList; import java.util.HashMap; import com.opendoorlogistics.api.tables.ODLColumnType; import com.opendoorlogistics.api.tables.ODLTableReadOnly; import com.opendoorlogistics.api.tables.TableFlags; import com.opendoorlogistics.core.tables.ColumnValueProcessor; import com.opendoorlogistics.core.tables.utils.TableFlagUtils; import com.opendoorlogistics.core.tables.utils.TableUtils; import com.opendoorlogistics.core.utils.strings.Strings; final public class ColumnIndex { private HashMap<Object, TLongArrayList> index; private ODLColumnType lastColumnType; private IndexState state = IndexState.DISABLED; enum IndexState{ DISABLED, PENDING, ACTIVE } void insert(long rowId, Object value, ODLTableReadOnly table, int colIndx){ updateState(table, colIndx); if(state == IndexState.ACTIVE){ put(value, rowId, table.getColumnType(colIndx)); } } void remove(long rowId,Object value,ODLTableReadOnly table, int colIndx){ updateState(table, colIndx); if(state == IndexState.ACTIVE){ internalRemove(rowId, value); } } /** * Change the value in the index. It is assumed the input value * is of the correct type. * @param rowId * @param previousValue * @param newValue * @param table * @param colIndx */ void set(long rowId,Object previousValue, Object newValue, ODLTableReadOnly table, int colIndx){ updateState(table, colIndx); if(state == IndexState.ACTIVE){ internalRemove(rowId, previousValue); put(newValue, rowId, table.getColumnType(colIndx)); } } private void internalRemove(long id ,Object value){ if(value!=null && String.class.isInstance(value)){ value = Strings.std(value.toString()); } TLongArrayList previous = index.get(value); if(previous!=null){ previous.remove(id); if(previous.size()==0){ index.remove(value); } } } /** * Find all row ids with the value or return an empty collection * if none found, using the index if available * @param table * @param colIndx * @param value * @return */ long[] find(ODLTableReadOnly table,int colIndx, Object value){ updateState(table, colIndx); if(state == IndexState.PENDING){ // active the hashmap buildHashmap(table, colIndx); state = IndexState.ACTIVE; } long[] ret =null; if(state == IndexState.ACTIVE){ // convert the search value to the type and standardise if its a string ODLColumnType colType = table.getColumnType(colIndx); Object converted = ColumnValueProcessor.convertToMe(colType,value); if(colType == ODLColumnType.STRING && converted!=null){ converted = Strings.std(converted.toString()); } // only search if the value converts to the column type if((value!=null && converted==null)==false){ TLongArrayList list = index.get(converted); if(list!=null){ ret = list.toArray(); }else{ return new long[0]; } } } else{ ret = TableUtils.find(table, colIndx, value); } if(ret==null){ // create default object ret = new long[0]; } return ret; } private void updateState(ODLTableReadOnly table, int colIndx){ // check for change of type ODLColumnType colType = table.getColumnType(colIndx); boolean changedType = lastColumnType != colType; lastColumnType = colType; // get the new state if(colType!=ODLColumnType.DOUBLE && colType!=ODLColumnType.STRING && colType!=ODLColumnType.COLOUR && colType!=ODLColumnType.LONG){ // non indexable type state = IndexState.DISABLED; } else if(TableFlagUtils.hasFlag(table.getColumnFlags(colIndx), TableFlags.FLAG_COLUMN_NOT_INDEXED)){ // column marked as shouldn't be indexed state = IndexState.DISABLED; }else if(state!=IndexState.ACTIVE){ state = IndexState.PENDING; } // clear the hashmap if not being used if(state!=IndexState.ACTIVE){ index = null; } // update the hashmap if being used and type has changed if(state==IndexState.ACTIVE && changedType){ buildHashmap(table, colIndx); } } private void buildHashmap(ODLTableReadOnly table, int colIndx){ int nr = table.getRowCount(); index = new HashMap<>(nr); ODLColumnType coltype = table.getColumnType(colIndx); for(int row =0 ; row<nr;row++){ Object val = table.getValueAt(row, colIndx); long id = table.getRowId(row); put(val, id,coltype); } } private void put(Object val, long id, ODLColumnType columnType) { if(columnType == ODLColumnType.STRING && val!=null){ val = Strings.std(val.toString()); } TLongArrayList list = index.get(val); if(list==null){ list = new TLongArrayList(1); index.put(val, list); } list.add(id); } }