/*******************************************************************************
* 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.utils;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.procedure.TIntObjectProcedure;
import gnu.trove.set.hash.TLongHashSet;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import com.opendoorlogistics.api.ExecutionReport;
import com.opendoorlogistics.api.components.ODLComponent;
import com.opendoorlogistics.api.tables.HasFlags;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLDatastoreUndoable;
import com.opendoorlogistics.api.tables.ODLTable;
import com.opendoorlogistics.api.tables.ODLTableAlterable;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.api.tables.ODLTableDefinitionAlterable;
import com.opendoorlogistics.api.tables.ODLTableReadOnly;
import com.opendoorlogistics.api.tables.TableFlags;
import com.opendoorlogistics.core.scripts.wizard.TagUtils;
import com.opendoorlogistics.core.tables.ColumnValueProcessor;
import com.opendoorlogistics.core.tables.ODLRowReadOnly;
import com.opendoorlogistics.core.tables.decorators.rows.ODLRowReadOnlyImpl;
import com.opendoorlogistics.core.tables.memory.ODLTableImpl;
import com.opendoorlogistics.core.utils.Long2Ints;
import com.opendoorlogistics.core.utils.strings.Strings;
final public class TableUtils {
/**
* Find all row ids with the value or return an empty collection
* if none found.
* @param table
* @param colIndx
* @param value
* @return
*/
public static long[] find(ODLTableReadOnly table,int colIndx, Object value){
TLongArrayList ret = new TLongArrayList();
ODLColumnType colType = table.getColumnType(colIndx);
Object converted = ColumnValueProcessor.convertToMe(colType,value);
if(value!=null && converted==null){
// doesn't convert to the column type...
return ret.toArray();
}
int nr = table.getRowCount();
for(int row =0 ; row< nr;row++){
Object compare = table.getValueAt(row, colIndx);
if(ColumnValueProcessor.isEqual(compare, converted)){
ret.add(table.getRowId(row));
}
}
return ret.toArray();
}
/**
* Add tables from one datastore to another.
* @param addToThis
* @param addThis
* @param makeTableNamesUnique
* @return
*/
public static boolean addDatastores(final ODLDatastoreUndoable<? extends ODLTableAlterable> addToThis,final ODLDatastore<? extends ODLTableReadOnly> addThis, final boolean makeTableNamesUnique){
return runTransaction(addToThis, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
for(int i =0 ;i< addThis.getTableCount() ; i++){
ODLTableReadOnly table = addThis.getTableAt(i);
String name = table.getName();
if(findTable(addToThis, table.getName())!=null){
if(makeTableNamesUnique==false){
return false;
}
name = getUniqueNumberedTableName(name, addToThis);
}
if(DatastoreCopier.copyTable(table, addToThis, name)==null){
return false;
}
}
return true;
}
});
}
public static void addColumnFlag(ODLTableDefinitionAlterable dfn, int col , long flag){
dfn.setColumnFlags(col, dfn.getColumnFlags(col)|flag);
}
// /**
// *
// * @param dfn
// * @param flag
// */
// public static void addColumnFlag(ODLTableDefinitionAlterable dfn, long flag){
// for(int i =0 ; i<dfn.getColumnCount();i++){
// addColumnFlag(dfn, i, flag);
// }
// }
public static int addRow(ODLTable table,Object...values){
int row = table.createEmptyRow(-1);
for(int col =0 ; col<values.length ; col++){
table.setValueAt(values[col], row,col);
}
return row;
}
public static int addColumn(ODLTableDefinitionAlterable dfn,String name, ODLColumnType type, long flags, String description, String ...tags){
int col = dfn.addColumn(-1,name, type, flags);
if( col!=-1){
int indx = dfn.getColumnCount()-1;
if(description!=null){
dfn.setColumnDescription(indx, description);
}
if(tags.length>0){
dfn.setColumnTags(indx,TagUtils.createTagSet(tags));
}
return col;
}
return col;
}
public static boolean runTransaction(ODLDatastore<? extends ODLTableDefinition> ds,Callable<Boolean> callable) {
return runTransaction(ds, callable, null);
}
public static boolean runTransaction(ODLDatastore<? extends ODLTableDefinition> ds,Callable<Boolean> callable, ExecutionReport report) {
return runTransaction(ds, callable, report, false);
}
/**
* Run the callable in a transaction if the datastore isn't already in one.
* @param ds
* @param callable
* @return
*/
public static boolean runTransaction(ODLDatastore<? extends ODLTableDefinition> ds,Callable<Boolean> callable, ExecutionReport report, boolean throwExceptions) {
boolean started=false;
if(!ds.isInTransaction()){
ds.startTransaction();
started = true;
}
try {
if (callable.call()) {
if(started){
ds.endTransaction();
}
return true;
}
} catch (Throwable e2) {
if(report!=null){
report.setFailed(e2);
}
if(throwExceptions){
// Can only throw an unchecked exception and its better (for debugging etc) to rethrow the original
if(e2 instanceof RuntimeException){
throw (RuntimeException)e2;
}
else{
throw new RuntimeException(e2);
}
}
}
if(started){
ds.rollbackTransaction();
}
return false;
}
public static TreeMap<String, Integer> countObjectsByTableName(ODLDatastore<? extends ODLTableReadOnly> ds,long [] ids){
TreeMap<String, Integer> ret = new TreeMap<>();
for(long id: ids){
int tableId = getTableId(id);
ODLTableReadOnly table = ds.getTableByImmutableId(tableId);
if(table!=null){
Integer current = ret.get(table.getName());
if(current==null){
current=0;
}
current++;
ret.put(table.getName(), current);
}
}
return ret;
}
/**
* Find the first worksheet in the spreadsheet with the matching name. if strings are standardised we ignore case and trailing spaces,
*
* @param database
* @param name
* @param standardiseStrings
* @return
*/
private static <T extends ODLTableDefinition> int internalFindTableIndex(ODLDatastore<T> database, String name, boolean standardiseStrings) {
// handle null by assuming its empty
if(name==null){
name = "";
}
if (standardiseStrings) {
name = Strings.std(name);
}
for (int i = 0; i < database.getTableCount(); i++) {
T sheet = database.getTableAt(i);
String sheetName = sheet.getName();
if (standardiseStrings) {
sheetName = Strings.std(sheetName);
}
if (name.equals(sheetName)) {
return i;
}
}
return -1;
}
/**
* Gets the supported string representation for a value
* @param table
* @param row
* @param col
* @return
*/
public static String getValueAsString(ODLTableReadOnly table, int row, int col){
return (String)ColumnValueProcessor.convertToMe(ODLColumnType.STRING,table.getValueAt(row, col),table.getColumnType(col));
}
public static <T extends ODLTableDefinition> int findTableIndexWithFlag(ODLDatastore<T> database,long flag) {
for(int i =0 ; i<database.getTableCount();i++){
if((database.getTableAt(i).getFlags() & flag )==flag){
return i;
}
}
return -1;
}
public static <T extends ODLTableDefinition> int findTableIndex(ODLDatastore<T> database, String name, boolean standardiseStrings) {
int ret = internalFindTableIndex(database, name, false);
if (ret == -1 && standardiseStrings) {
ret = internalFindTableIndex(database, name, true);
}
return ret;
}
/**
* Find table by name, standardising the strings.
* @param database
* @param name
* @param standardiseStrings
* @return
*/
public static <T extends ODLTableDefinition> T findTable(ODLDatastore<T> database, String name) {
return findTable(database, name, true);
}
public static <T extends ODLTableDefinition> T findTable(ODLDatastore<T> database, String name, boolean standardiseStrings) {
int indx = findTableIndex(database, name, standardiseStrings);
if (indx != -1) {
return database.getTableAt(indx);
}
return null;
}
public static String [] getColumnNames(ODLTableDefinition defn){
String[] ret = new String[defn.getColumnCount()];
for(int i =0 ;i< defn.getColumnCount() ; i++){
ret[i] = defn.getColumnName(i);
}
return ret;
}
public static boolean isTableOptional(ODLTableDefinition defn) {
return (defn.getFlags() & TableFlags.FLAG_IS_OPTIONAL) != 0;
}
/**
* Returns true if all values in the column are numeric or null / empty strings
* @param table
* @param col
* @return
*/
public static boolean isColumnValuesNumeric(ODLTableReadOnly table, int col){
if(ColumnValueProcessor.isNumeric(table.getColumnType(col))){
return true;
}
int nr = table.getRowCount();
for(int i =0 ; i < nr ; i++){
Object val = table.getValueAt(i, col);
if(val!=null){
if(Strings.isNumber(val.toString())==false){
return false;
}
}
}
return true;
}
public static boolean isColumnOptional(ODLTableDefinition defn, int colIndx) {
return (defn.getColumnFlags(colIndx) & TableFlags.FLAG_IS_OPTIONAL) != 0;
}
/**
* Return the first column with the matching type or -1 if not found
* @param table
* @param type
* @return
*/
public static int findColumnIndx(ODLTableDefinition table, ODLColumnType type){
int n = table.getColumnCount();
for(int i = 0; i < n ;i++){
if(table.getColumnType(i)==type){
return i;
}
}
return -1;
}
public static int findColumnIndx(ODLTableDefinition table, String name) {
return findColumnIndx(table, name, true);
}
public static int findColumnIndx(ODLTableDefinition table, String name, boolean standardiseStrings) {
int nbCols = table.getColumnCount();
for (int i = 0; i < nbCols; i++) {
if (table.getColumnName(i).equals(name)) {
return i;
}
}
if (standardiseStrings) {
for (int i = 0; i < nbCols; i++) {
if (Strings.equalsStd(table.getColumnName(i), name)) {
return i;
}
}
}
return -1;
}
public static int countNbColumnsWithFlag(ODLTableDefinition table, long flag){
int nc = table.getColumnCount();
int ret=0;
for(int i =0 ; i<nc;i++){
if( (table.getColumnFlags(i) & flag)==flag){
ret++;
}
}
return ret;
}
public static int findColumnIndexWithFlag(ODLTableDefinition table, long flag){
int nc = table.getColumnCount();
for(int i =0 ; i<nc;i++){
if( (table.getColumnFlags(i) & flag)==flag){
return i;
}
}
return -1;
}
public static String convertToString(ODLDatastore<?> db) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < db.getTableCount(); i++) {
if (i > 0) {
builder.append(System.lineSeparator());
}
builder.append(db.getTableAt(i).toString());
}
return builder.toString();
}
public static String convertToString(ODLTableDefinition tm, boolean includeDebugInfo) {
StringBuilder builder = new StringBuilder();
if(includeDebugInfo){
builder.append("Table " + tm.getName() + System.lineSeparator());
}
// write header
int nc = tm.getColumnCount();
if(includeDebugInfo){
builder.append("RowID");
}
for (int col = 0; col < nc; col++) {
if(includeDebugInfo || col>0){
builder.append("\t");
}
if(includeDebugInfo){
builder.append(tm.getColumnType(col) + " ");
}
builder.append(tm.getColumnName(col) != null ? tm.getColumnName(col) : "");
if(includeDebugInfo){
builder.append(" (" + tm.getColumnImmutableId(col) + ")");
}
}
builder.append(System.lineSeparator());
return builder.toString();
}
public static String convertToString(ODLTableReadOnly tm) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(baos);
writeToStringStream(tm, pw,true);
pw.flush();
return baos.toString();
}
public static void writeToStringStream(ODLTableReadOnly tm, PrintWriter writer, boolean includeDebugInfo) {
// write header
writer.append(convertToString((ODLTableDefinition) tm,includeDebugInfo));
// write rows
int nc = tm.getColumnCount();
int nr = tm.getRowCount();
for (int row = 0; row < nr; row++) {
if(includeDebugInfo){
writer.append(Long.toString(tm.getRowId(row)));
}
for (int col = 0; col < nc; col++) {
if(col>0 || includeDebugInfo){
writer.append("\t");
}
Object o = tm.getValueAt(row, col);
writer.append(o != null ? getValueAsString(tm, row, col) : "");
}
writer.append(System.lineSeparator());
}
}
public static void createRows(Object[][] objs, ODLTable table) {
for (Object[] obj : objs) {
int indx = table.createEmptyRow(-1);
for (int i = 0; i < obj.length; i++) {
table.setValueAt(obj[i], indx, i);
}
}
}
public static Object createDefaultConfig(ODLComponent component) {
if (component != null && component.getConfigClass() != null) {
try {
return component.getConfigClass().newInstance();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
return null;
}
// public static <TFrom extends ODLTableDefinition> ODLDatastore<ODLTableReadOnly> castCopyToODLTableReadOnly(ODLDatastore<TFrom> from) {
// ODLDatastoreImpl<ODLTableReadOnly> ret = new ODLDatastoreImpl<>(null);
// for (int i = 0; i < from.getTableCount(); i++) {
// ret.addTable((ODLTableReadOnly) from.getTableAt(i));
// }
// return ret;
// }
// public static <TFrom extends ODLTableDefinition> ODLDatastore<ODLTable> castCopyToODLTable(ODLDatastore<TFrom> from) {
// ODLDatastoreImpl<ODLTable> ret = new ODLDatastoreImpl<>(null);
// for (int i = 0; i < from.getTableCount(); i++) {
// ret.addTable((ODLTable) from.getTableAt(i));
// }
// return ret;
// }
// public static <TFrom extends ODLTableDefinition> ODLDatastore<ODLTableDefinition> castCopyToODLTableDefinition(ODLDatastore<TFrom> from) {
// ODLDatastoreImpl<ODLTableDefinition> ret = new ODLDatastoreImpl<>(null);
// for (int i = 0; i < from.getTableCount(); i++) {
// ret.addTable((ODLTable) from.getTableAt(i));
// }
// return ret;
//
// }
private static final Random idGenerator = new Random(123);
/**
* Create a unique id. The use of a random number also ensures the table id is extremely unlikely
* to ever be reused in the datastore (but does not guarantee this).
*
* @param ds
* @return
*/
public static int createUniqueTableId(ODLDatastore<? extends ODLTableDefinition> ds) {
int id = idGenerator.nextInt();
while (ds.getTableByImmutableId(id) != null) {
id = idGenerator.nextInt();
}
return id;
}
public static String getUniqueNumberedColumnName(String prefix, ODLTableDefinition dfn) {
int i = 1;
int nbCols = dfn.getColumnCount();
while (true) {
String name = prefix + Integer.toString(i);
boolean found = false;
for (int j = 0; j < nbCols && !found; j++) {
found = Strings.equalsStd(name, dfn.getColumnName(j));
}
if (!found) {
return name;
}
i++;
}
}
public static String getUniqueNumberedTableName(String prefix, ODLDatastore<? extends ODLTableDefinition> dfn) {
int i = 1;
int nbTables = dfn.getTableCount();
while (true) {
String name = prefix + Integer.toString(i);
boolean found = false;
for (int j = 0; j < nbTables && !found; j++) {
found = Strings.equalsStd(name, dfn.getTableAt(j).getName());
}
if (!found) {
return name;
}
i++;
}
}
/**
* Remove all rows in the most efficient manner - deleting
* the end one each time.
* @param table
*/
public static void removeAllRows(ODLTable table){
while(table.getRowCount()>0){
table.deleteRow(table.getRowCount()-1);
}
}
private static boolean isEmpty(Object o){
return o == null || o.toString().length()==0;
}
/**
* Sort the input table by the input columns, performing a numeric
* sort if the field is numeric or if the string representation for
* each field value yields a number.
* @param table
* @param cols
*/
public static void sort(ODLTable table, final SortColumn [] cols){
final boolean [] numeric = new boolean[cols.length];
for(int i =0 ; i < cols.length ; i++){
numeric[i] = isColumnValuesNumeric(table, cols[i].getIndx());
}
sort(table, new Comparator<ODLRowReadOnly>(){
@Override
public int compare(ODLRowReadOnly o1, ODLRowReadOnly o2) {
int diff=0;
for(int i =0 ; i < cols.length && diff==0; i++){
int col = cols[i].getIndx();
boolean isNumeric = numeric[i];
diff = internalCompareRowElement(o1, o2, col, isNumeric);
if(diff!=0 && cols[i].isAscending()==false){
diff = -diff;
}
}
return diff;
}
});
}
public static void sort(ODLTable table, Comparator<ODLRowReadOnly> comparator){
// copy and remove all using a temporary table
ODLTableImpl copy = new ODLTableImpl(0, table.getName());
DatastoreCopier.copyTableDefinition(table, copy);
DatastoreCopier.copyData(table, copy);
removeAllRows(table);
// do basic (a.k.a. slow) insertion sort
for(int i =0 ; i < copy.getRowCount() ; i++){
int insertAt=-1;
ODLRowReadOnlyImpl inserting = new ODLRowReadOnlyImpl(copy, i);
int nr = table.getRowCount();
if(nr==0){
insertAt=0;
}else{
for(int j =0 ; j < nr ; j++){
// do comparison
ODLRowReadOnlyImpl comparing = new ODLRowReadOnlyImpl(table, j);
if(comparator.compare(inserting, comparing)<=0){
insertAt = j;
break;
}
}
}
if(insertAt==-1){
// insert at end
insertAt = nr;
}
DatastoreCopier.insertRow(copy, i, table, insertAt);
}
}
public static String[] getAlphabeticallySortedTableNames(ODLDatastore<? extends ODLTableDefinition> ds){
List<ODLTableDefinition> list = getAlphabeticallySortedTables(ds);
String[]ret = new String[list.size()];
for(int i =0 ; i<ret.length ; i++){
ret[i] = list.get(i).getName();
}
return ret;
}
public static List<ODLTableDefinition> getAlphabeticallySortedTables(ODLDatastore<? extends ODLTableDefinition> ds){
ArrayList<ODLTableDefinition> ret = new ArrayList<>(ds.getTableCount());
for(int i =0 ; i < ds.getTableCount() ; i++){
ret.add(ds.getTableAt(i));
}
Collections.sort(ret,new Comparator<ODLTableDefinition>(){
@Override
public int compare(ODLTableDefinition o1, ODLTableDefinition o2) {
return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase());
}
});
return ret;
}
// public static TreeMap<ODLRowReadOnly, List<ODLRowReadOnly>> groupBy(ODLTableReadOnly table,final int [] groupFields){
// List<ODLRowReadOnly> rows = toRowsView(table);
//
// // find if columns are numeric
// final boolean [] numeric = new boolean[table.getColumnCount()];
// for(int i =0 ; i < numeric.length ; i++){
// numeric[i] = isColumnValuesNumeric(table, i);
// }
//
// // create treemap with custom comparator which understands the grouping
// TreeMap<ODLRowReadOnly, List<ODLRowReadOnly>> ret = new TreeMap<>(new Comparator<ODLRowReadOnly>(){
//
// @Override
// public int compare(ODLRowReadOnly o1, ODLRowReadOnly o2) {
// int diff=0;
// for(int i =0 ; i < groupFields.length && diff==0; i++){
// int col = groupFields[i];
// boolean isNumeric = numeric[col];
//
// diff = internalCompareRowElement(o1, o2, col, isNumeric);
//
//// if(diff!=0 && cols[i].isAscending()==false){
//// diff = -diff;
//// }
// }
// return diff;
// }
//
// });
//
// // group the rows
// for(ODLRowReadOnly row : rows){
// List<ODLRowReadOnly> list = ret.get(row);
// if(list==null){
// list = new ArrayList<>();
// ret.put(row, list);
// }
//
// list.add(row);
// }
//
// return ret;
// }
public static List<ODLRowReadOnly> toRowsView(ODLTableReadOnly table) {
List<ODLRowReadOnly> rows = new ArrayList<>();
int nr = table.getRowCount();
for(int i = 0 ; i < nr ; i++){
ODLRowReadOnlyImpl row = new ODLRowReadOnlyImpl(table, i);
rows.add(row);
}
return rows;
}
/**
* Use the column type to compare an element in the row
* @param o1
* @param o2
* @param col
* @return
*/
public static int compareRowElementUsingColumnType(ODLRowReadOnly o1, ODLRowReadOnly o2, int col){
int diff=0;
Object val1 = o1.get(col);
Object val2 = o2.get(col);
boolean empty1 = isEmpty(val1);
boolean empty2 = isEmpty(val2);
diff = Boolean.compare(empty1, empty2);
if(diff==0 && val1!=null){
ODLColumnType ct = o1.getDefinition().getColumnType(col);
if(ct!=o2.getDefinition().getColumnType(col)){
throw new RuntimeException("Rows have different column types");
}
diff = ColumnValueProcessor.compareSameType(ct, val1, val2);
}
return diff;
}
public static int compareRowUsingColumnType(ODLRowReadOnly o1, ODLRowReadOnly o2){
int nc = o1.getColumnCount();
if(nc!=o2.getColumnCount()){
throw new RuntimeException("Rows have different number of columns");
}
int diff=0;
for(int col =0 ; col < nc && diff==0 ; col++){
diff = compareRowElementUsingColumnType(o1, o2, col);
}
return diff;
}
public static void createFilledRow(ODLTable table, Object...vals){
int row = table.createEmptyRow(-1);
if(vals.length>table.getColumnCount()){
throw new RuntimeException();
}
for(int i = 0;i < vals.length ;i++){
table.setValueAt(vals[i], row, i);
}
}
public static Comparator<ODLRowReadOnly> createRowComparatorUsingColumnType(){
return new Comparator<ODLRowReadOnly>() {
@Override
public int compare(ODLRowReadOnly o1, ODLRowReadOnly o2) {
return compareRowUsingColumnType(o1, o2);
}
};
}
public static Object [] getRowValues(ODLTableReadOnly table, int row){
int nc = table.getColumnCount();
Object[]ret = new Object[nc];
for(int i =0 ; i<nc;i++){
ret[i] = table.getValueAt(row, i);
}
return ret;
}
public static Object [][] getTableValues(ODLTableReadOnly table){
int nr = table.getRowCount();
Object[][]ret = new Object[nr][];
for(int row=0;row<nr;row++){
ret[row] = getRowValues(table, row);
}
return ret;
}
private static int internalCompareRowElement(ODLRowReadOnly o1, ODLRowReadOnly o2, int col, boolean isNumeric) {
// sort empty last when ascending
Object val1 = o1.get(col);
Object val2 = o2.get(col);
return ColumnValueProcessor.compareValues(val1, val2, isNumeric);
}
// public static int compareValues(Object val1, Object val2, boolean isNumeric) {
// int diff;
// boolean empty1 = isEmpty(val1);
// boolean empty2 = isEmpty(val2);
// diff = Boolean.compare(empty1, empty2);
//
// if(diff==0 && val1!=null){
// if(isNumeric){
// Double d1 = (Double)ColumnValueProcessor.convertToMe(ODLColumnType.DOUBLE,val1);
// Double d2 = (Double)ColumnValueProcessor.convertToMe(ODLColumnType.DOUBLE,val2);
// diff = d1.compareTo(d2);
// }else{
// // ignore case in string compare
// String s1 = val1.toString().toLowerCase();
// String s2 = val2.toString().toLowerCase();
// diff = s1.compareTo(s2);
// }
// }
// return diff;
// }
public static String getShortTableDescription(ODLTableDefinition item) {
StringBuilder ret = new StringBuilder();
ret.append("Table \"" + item.getName() + "\"");
if(item.getColumnCount()>0){
ret.append(", columns ");
for(int i =0 ; i < item.getColumnCount() ; i++){
if(i>0){
ret.append(", ");
}
ret.append("\"" + item.getColumnName(i) + "\"");
}
}
return ret.toString();
}
public static int getTableId(long globalRowId){
return Long2Ints.getFirst(globalRowId);
}
public static TIntObjectMap<TLongArrayList> splitGlobalIdsByTable(long ...ids){
TIntObjectMap<TLongArrayList> ret = new TIntObjectHashMap<TLongArrayList>();
for(long id : ids){
int tableId = getTableId(id);
TLongArrayList list = ret.get(tableId);
if(list==null){
list =new TLongArrayList();
ret.put(tableId, list);
}
list.add(id);
}
return ret;
}
public static void deleteByGlobalId(final ODLDatastore<? extends ODLTable> ds, boolean useTransaction, long...ids){
if(useTransaction){
ds.startTransaction();
}
try {
// split by table id
TIntObjectMap<TLongArrayList> byTable =splitGlobalIdsByTable(ids);
byTable.forEachEntry(new TIntObjectProcedure<TLongArrayList>() {
@Override
public boolean execute(int tableId, TLongArrayList rowIds) {
ODLTable table = ds.getTableByImmutableId(tableId);
if(table!=null){
deleteById(table, rowIds.toArray());
}
return true;
}
});
}finally{
if(useTransaction){
ds.endTransaction();
}
}
}
/**
* Deleting by id is slow - it can take O(n) for each id as it requires finding
* the row for each and and removing each from the array.
* @param ds
* @param ids
*/
public static void deleteById(ODLTable table, long...ids){
// find indices to delete
TLongHashSet set = new TLongHashSet(ids);
int nr = table.getRowCount();
TIntArrayList list = new TIntArrayList();
for(int row =0;row<nr;row++){
long rowid = table.getRowId(row);
if(set.contains(rowid)){
list.add(row);
}
}
// delete in reverse order so indices are still valid
for(int i = list.size()-1; i>=0 ; i--){
int row = list.get(i);
table.deleteRow(row);
}
}
// /**
// * This is slow, O(n).
// * @param findId
// * @return
// */
// public static int findRowIndex(ODLTable table,int findId){
// int nr = table.getRowCount();
// for(int row =0;row<nr;row++){
// int rowid = table.getRowLocalId(row);
// if(rowid==findId){
// return row;
// }
// }
// return -1;
// }
//
// public int [] globalRowIdsToLocal(long ...globalIds){
// int[]ret = new int[globalIds.length];
// for(int i =0 ;i<ret.length ;i++){
// ret[i] = getLocalRowId(globalIds[i]);
// }
// return ret;
// }
//
public static List<String> getTableNames(ODLDatastore<? extends ODLTableDefinition>ds){
ArrayList<String> ret = new ArrayList<>();
for(ODLTableDefinition dfn:getTables(ds)){
ret.add(dfn.getName());
}
return ret;
}
/**
* Get iterable from the tables in the datastore.
* Returns an iterable with zero elements if input datastore is null.
* Changes to the datastore will results in changes to the iterable.
* @param ds
* @return
*/
public static <T extends ODLTableDefinition> Iterable<T> getTables(final ODLDatastore<T> ds){
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
int next=0;
@Override
public boolean hasNext() {
return ds!=null && next < ds.getTableCount();
}
@Override
public T next() {
return ds.getTableAt(next++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
public static boolean hasFlag(HasFlags dfn, long flag){
return (dfn.getFlags() & flag)==flag;
}
public static int getRowCount(ODLDatastore<? extends ODLTableReadOnly> ds){
int ret=0;
for(int i =0 ; i<ds.getTableCount() ; i++){
ret += ds.getTableAt(i).getRowCount();
}
return ret;
}
public static Iterator<ODLRowReadOnly> readOnlyIterator(final ODLTableReadOnly table){
return new Iterator<ODLRowReadOnly>() {
private int row=-1;
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public ODLRowReadOnly next() {
row++;
return new ODLRowReadOnlyImpl(table, row);
}
@Override
public boolean hasNext() {
return row < table.getRowCount()-1;
}
};
}
public static Iterable<ODLRowReadOnly> readOnlyIterable(final ODLTableReadOnly table){
return new Iterable<ODLRowReadOnly>() {
@Override
public Iterator<ODLRowReadOnly> iterator() {
return readOnlyIterator(table);
}
};
}
public static void addTableFlags(ODLTableDefinitionAlterable table, long flags){
long current = table.getFlags();
table.setFlags(flags | current);
}
public static void removeTableFlags(ODLTableDefinitionAlterable table, long flags){
long current = table.getFlags();
table.setFlags( (~flags) & current);
}
public static int getLocalRowId(long globalRowId) {
return Long2Ints.getSecond(globalRowId);
}
public static long getGlobalId(int tableId, int rowId) {
return Long2Ints.get(tableId, rowId);
}
public static void removeAllUIEditFlags(ODLDatastore<? extends ODLTableAlterable> ds){
ds.setFlags(ds.getFlags() & ~TableFlags.UI_EDIT_PERMISSION_FLAGS);
for(int i =0 ; i<ds.getTableCount();i++){
ODLTableDefinitionAlterable table = ds.getTableAt(i);
if(table!=null){
table.setFlags(table.getFlags() & ~TableFlags.UI_EDIT_PERMISSION_FLAGS);
}
}
}
}