/*******************************************************************************
* 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.api.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.opendoorlogistics.api.ExecutionReport;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.Tables;
import com.opendoorlogistics.api.tables.HasUndoStateListeners;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLDatastoreAlterable;
import com.opendoorlogistics.api.tables.ODLDatastoreUndoable;
import com.opendoorlogistics.api.tables.ODLFlatDatastoreExt;
import com.opendoorlogistics.api.tables.ODLListener;
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.api.tables.beans.BeanMappedRow;
import com.opendoorlogistics.api.tables.beans.BeanTableMapping;
import com.opendoorlogistics.core.scripts.elements.AdapterConfig;
import com.opendoorlogistics.core.scripts.execution.adapters.AdapterBuilderUtils;
import com.opendoorlogistics.core.scripts.wizard.ColumnNameMatch;
import com.opendoorlogistics.core.tables.ODLFactory;
import com.opendoorlogistics.core.tables.beans.BeanMapping;
import com.opendoorlogistics.core.tables.beans.BeanTypeConversion;
import com.opendoorlogistics.core.tables.decorators.tables.FlatDs2TableObject;
import com.opendoorlogistics.core.tables.utils.DatastoreComparer;
import com.opendoorlogistics.core.tables.utils.DatastoreCopier;
import com.opendoorlogistics.core.tables.utils.ExampleData;
import com.opendoorlogistics.core.tables.utils.ParametersTable;
import com.opendoorlogistics.core.tables.utils.TableUtils;
import com.opendoorlogistics.core.utils.strings.EnumStdLookup;
public class TablesImpl implements Tables {
private static final EnumStdLookup<ODLColumnType> CT_LOOKUP = new EnumStdLookup<ODLColumnType>(ODLColumnType.class);
private final ODLApi api;
public TablesImpl(ODLApi api) {
this.api = api;
}
@Override
public ODLTableDefinitionAlterable copyTableDefinition(ODLTableDefinition copyThis, ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> copyTo) {
return DatastoreCopier.copyTableDefinition(copyThis, copyTo, copyThis.getName(), copyTo.getTableByImmutableId(copyThis.getImmutableId()) == null ? copyThis.getImmutableId() : -1);
}
@Override
public int addRow(ODLTable table, Object... values) {
return TableUtils.addRow(table, values);
}
@Override
public ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> createDefinitionDs() {
return ODLFactory.createDefinition();
}
@Override
public ODLDatastoreAlterable<? extends ODLTableAlterable> createAlterableDs() {
return ODLFactory.createAlterable();
}
@Override
public ODLTableAlterable createAlterableTable(String name) {
return ODLFactory.createAlterableTable(name);
}
@Override
public void setColumnIsOptional(ODLTableDefinitionAlterable table, int col, boolean optional) {
long flags = table.getColumnFlags(col);
if (optional) {
flags |= TableFlags.FLAG_IS_OPTIONAL;
} else {
flags &= ~TableFlags.FLAG_IS_OPTIONAL;
}
table.setColumnFlags(col, flags);
}
@Override
public void clearTable(ODLTable table) {
TableUtils.removeAllRows(table);
}
@Override
public void validateForeignKey(ODLTableReadOnly primaryKeyTable, int primaryKeyColIndx, ODLTable foreignKeyTable, int foreignKeyColIndx, KeyValidationMode mode) {
int row = 0;
while (row < foreignKeyTable.getRowCount()) {
Object value = foreignKeyTable.getValueAt(row, foreignKeyColIndx);
boolean missing = false;
if (value == null) {
missing = true;
}
if (!missing) {
long[] vals = primaryKeyTable.find(primaryKeyColIndx, value);
missing = vals == null || vals.length == 0;
}
if (missing) {
switch (mode) {
case REMOVE_CORRUPT_FOREIGN_KEY:
// delete and continue here so we don't increment row
foreignKeyTable.deleteRow(row);
continue;
case THROW_UNCHECKED_EXCEPTION:
throw new RuntimeException(
"Table \"" + foreignKeyTable.getName() + "\" has corrupt foreign key value. \"" + value + "\" cannot be found in table \"" + primaryKeyTable.getName() + "\".");
}
}
row++;
}
}
@Override
public int findColumnIndex(ODLTableDefinition table, String name) {
return TableUtils.findColumnIndx(table, name, true);
}
@Override
public <T extends ODLTableDefinition> T findTable(ODLDatastore<T> ds, String tableName) {
return TableUtils.findTable(ds, tableName, true);
}
@Override
public ODLColumnType getColumnType(Class<?> externalType) {
return BeanTypeConversion.getInternalType(externalType);
}
@Override
public int findTableIndex(ODLDatastore<? extends ODLTableDefinition> ds, String table) {
return TableUtils.findTableIndex(ds, table, true);
}
@Override
public ODLDatastore<? extends ODLTable> createExampleDs() {
return ExampleData.createTerritoriesExample(3);
}
@Override
public void copyRow(ODLTableReadOnly from, int rowIndex, ODLTable to) {
DatastoreCopier.insertRow(from, rowIndex, to, to.getRowCount());
}
@Override
public void copyRow(ODLTableReadOnly from, int fromRowIndex, ODLTable to, int toRowIndex) {
// copy values first in case we're inserting into same table at an earlier position
int n = from.getColumnCount();
Object [] vals = new Object[n];
for(int i =0 ; i<n;i++){
vals[i] = from.getValueAt(fromRowIndex, i);
}
to.insertEmptyRow(toRowIndex, -1);
for(int i =0 ; i<n;i++){
to.setValueAt(vals[0], toRowIndex, i);
}
}
@Override
public ODLTableAlterable createTable(ODLTableDefinition tableDefinition) {
ODLDatastoreAlterable<? extends ODLTableAlterable> ds = createAlterableDs();
DatastoreCopier.copyTableDefinition(tableDefinition, ds);
return ds.getTableAt(0);
}
@Override
public void copyRowById(ODLTableReadOnly from, long rowId, ODLTable to) {
DatastoreCopier.copyRowById(from, rowId, to);
}
@Override
public void addTableDefinitions(ODLDatastore<? extends ODLTableDefinition> schema, ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> ds, boolean changeFieldTypes) {
DatastoreCopier.enforceSchema(schema, ds, changeFieldTypes);
}
@Override
public void addTablesWithData(ODLDatastore<? extends ODLTableReadOnly> source, ODLDatastoreAlterable<? extends ODLTableAlterable> destination) {
DatastoreCopier.mergeAll(source, destination);
}
@Override
public void addTableDefinition(ODLTableDefinition schema, ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> ds, boolean changeFieldTypes) {
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> tempDs = createAlterableDs();
DatastoreCopier.copyTableDefinition(schema, tempDs);
addTableDefinitions(tempDs, ds, changeFieldTypes);
}
@Override
public ODLTableDefinition createParametersTableDefinition() {
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> tempDs = createAlterableDs();
DatastoreCopier.copyTableDefinition(ParametersTable.tableDefinition(), tempDs);
return tempDs.getTableAt(0);
}
@Override
public void copyColumnDefinition(ODLTableDefinition source, int sourceCol, ODLTableDefinitionAlterable destination) {
DatastoreCopier.copyColumnDefinition(source, destination, sourceCol, false);
}
@Override
public void copyColumnDefinition(ODLTableDefinition source, int sourceCol, ODLTableDefinitionAlterable destination, int destinationCol) {
DatastoreCopier.copyColumnDefinition(source, destination, sourceCol,destinationCol, false);
}
@Override
public ODLTableAlterable copyTable(ODLTableReadOnly copyThis, ODLDatastoreAlterable<? extends ODLTableAlterable> copyTo) {
return DatastoreCopier.copyTable(copyThis, copyTo);
}
@Override
public boolean isIdentical(ODLTableReadOnly a, ODLTableReadOnly b) {
return DatastoreComparer.isSame(a, b, 0);
}
@Override
public ODLDatastoreAlterable<? extends ODLTableAlterable> copyDs(ODLDatastore<? extends ODLTableReadOnly> ds) {
return DatastoreCopier.copyAll(ds);
}
@Override
public ODLColumnType getColumnType(String columnTypeName) {
return CT_LOOKUP.get(columnTypeName);
}
@Override
public Set<String> getColumnNamesSet(ODLTableDefinition table) {
Set<String> ret = api.stringConventions().createStandardisedSet();
int nc = table.getColumnCount();
for (int i = 0; i < nc; i++) {
ret.add(table.getColumnName(i));
}
return ret;
}
@Override
public Map<String, Integer> getColumnNamesMap(ODLTableDefinition table) {
Map<String, Integer> ret = api.stringConventions().createStandardisedMap();
int nc = table.getColumnCount();
for (int i = 0; i < nc; i++) {
ret.put(table.getColumnName(i), i);
}
return ret;
}
@Override
public void copyTableDefinition(ODLTableDefinition copyThis, ODLTableDefinitionAlterable copyInto) {
DatastoreCopier.copyTableDefinition(copyThis, copyInto);
}
@Override
public ODLDatastoreUndoable<ODLTableAlterable> unflattenDs(ODLFlatDatastoreExt flatDatastore) {
FlatDs2TableObject.Flat2DsTableCache tableCache = new FlatDs2TableObject.Flat2DsTableCache(flatDatastore);
return new ODLDatastoreUndoable<ODLTableAlterable>() {
@Override
public ODLTableAlterable getTableByImmutableId(int id) {
return tableCache.getTable(id);
}
@Override
public void setFlags(long flags) {
flatDatastore.setFlags(flags);
}
@Override
public ODLDatastoreAlterable<? extends ODLTableAlterable> deepCopyWithShallowValueCopy(boolean createLazyCopy) {
return flatDatastore.deepCopyWithShallowValueCopy(createLazyCopy);
}
@Override
public int getTableCount() {
return flatDatastore.getTableCount();
}
@Override
public ODLTableAlterable getTableAt(int i) {
int id = flatDatastore.getTableId(i);
return getTableByImmutableId(id);
}
@Override
public void addListener(ODLListener tml, int... tableIds) {
flatDatastore.addListener(tml, tableIds);
}
@Override
public void removeListener(ODLListener tml) {
flatDatastore.removeListener(tml);
}
@Override
public void disableListeners() {
flatDatastore.disableListeners();
}
@Override
public void enableListeners() {
flatDatastore.enableListeners();
}
@Override
public void startTransaction() {
flatDatastore.startTransaction();
}
@Override
public void endTransaction() {
flatDatastore.endTransaction();
}
@Override
public boolean isInTransaction() {
return flatDatastore.isInTransaction();
}
@Override
public void rollbackTransaction() {
flatDatastore.rollbackTransaction();
}
@Override
public boolean isRollbackSupported() {
return flatDatastore.isRollbackSupported();
}
@Override
public long getFlags() {
return flatDatastore.getFlags();
}
@Override
public ODLTableAlterable createTable(String tablename, int id) {
id = flatDatastore.createTable(tablename, id);
if(id!=-1){
return getTableByImmutableId(id);
}
return null;
}
@Override
public void deleteTableById(int tableId) {
flatDatastore.deleteTableById(tableId);
}
@Override
public boolean setTableName(int tableId, String newName) {
return flatDatastore.setTableName(tableId, newName);
}
@Override
public void undo() {
flatDatastore.undo();
}
@Override
public void redo() {
flatDatastore.redo();
}
@Override
public boolean hasRedo() {
return flatDatastore.hasRedo();
}
@Override
public boolean hasUndo() {
return flatDatastore.hasUndo();
}
@Override
public void addUndoStateListener(HasUndoStateListeners.UndoStateChangedListener<ODLTableAlterable> listener) {
flatDatastore.addUndoStateListener(listener);
}
@Override
public void removeUndoStateListener(HasUndoStateListeners.UndoStateChangedListener<ODLTableAlterable> listener) {
flatDatastore.removeUndoStateListener(listener);
}
@Override
public void clearUndoBuffer() {
flatDatastore.clearUndoBuffer();
}
};
}
@Override
public BeanTableMapping mapBeanToTable(Class<? extends BeanMappedRow> cls) {
return BeanMapping.buildTable(cls);
}
@Override
public <T extends ODLTableDefinition> List<T> getTables(ODLDatastore<T> ds) {
int n = ds.getTableCount();
ArrayList<T> ret = new ArrayList<>(n);
for(int i =0 ; i < n ; i++){
ret.add(ds.getTableAt(i));
}
return ret;
}
@Override
public boolean modifyColumn(int index, int newIndx, String newName, ODLColumnType newType, ODLTableAlterable table) {
return DatastoreCopier.modifyColumnWithoutTransaction(index, newIndx, newName, newType, table.getColumnFlags(index), table);
}
@Override
public void clearDatastore(ODLDatastoreAlterable<? extends ODLTableAlterable> ds) {
while(ds.getTableCount()>0){
// Turn off all linked excel flags to allow deletion.
// Deletion uses the undo / redo decorator which deletes row and columns first so
// we must remove linked flags from everywhere or deletion will throw an exception.
// Table flags first
ODLTableAlterable table = ds.getTableAt(0);
table.setFlags(table.getFlags() & ~TableFlags.ALL_LINKED_EXCEL_FLAGS);
// Then table column flags
for(int col =0 ; col < table.getColumnCount() ; col++){
table.setColumnFlags(col, table.getColumnFlags(col) & ~TableFlags.ALL_LINKED_EXCEL_FLAGS);
}
// Then row flags
for(int row =0 ; row < table.getRowCount() ; row++){
long id = table.getRowId(row);
table.setRowFlags(table.getRowFlags(id) & ~TableFlags.ALL_LINKED_EXCEL_FLAGS, id);
}
// Finally delete the table itself
ds.deleteTableById(table.getImmutableId());
}
}
@Override
public void copyDs(ODLDatastore<? extends ODLTableReadOnly> copyFrom, ODLDatastoreAlterable<? extends ODLTableAlterable> copyTo, long rowFlagsToCopy) {
for(ODLTableReadOnly tableToCopy: getTables(copyFrom)){
// don't duplicate table names...
if(findTable(copyTo, tableToCopy.getName())!=null){
continue;
}
ODLTableAlterable copiedTable = copyTable(tableToCopy,copyTo);
// preserve table level flags
copiedTable.setFlags( tableToCopy.getFlags());
// preserve column flags
int nc = tableToCopy.getColumnCount();
for(int col=0;col < nc ; col++){
long colFlags = tableToCopy.getColumnFlags(col);
copiedTable.setColumnFlags(col, colFlags);
}
// preserve the row flags we're told to...
int nr=tableToCopy.getRowCount();
for(int row=0;row< nr ; row++){
long originalRowId = tableToCopy.getRowId(row);
long copiedRowId = copiedTable.getRowId(row);
long flags = tableToCopy.getRowFlags(originalRowId);
flags &= rowFlagsToCopy;
copiedTable.setRowFlags(flags, copiedRowId);
}
}
}
@Override
public boolean getTableDefinitionExists(ODLTableDefinition dfn, ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> ds, boolean checkFieldTypes, ExecutionReport report) {
ODLTableDefinition found = findTable(ds, dfn.getName());
if(found==null){
if(report!=null){
report.log("Table " + dfn.getName() + " was not found.");
}
return false;
}
ColumnNameMatch matcher = new ColumnNameMatch(dfn, found);
if(matcher.getUnmatchedInA().size()>0){
if(report!=null){
StringBuilder builder = new StringBuilder();
builder.append("The following field(s) were not found in table " + dfn.getName() + ": " );
int count=0;
for(int i : matcher.getUnmatchedInA().toArray()){
if(count>0){
builder.append(", ");
}
builder.append(dfn.getColumnName(i));
count++;
}
report.log(builder.toString());
}
return false;
}
if(checkFieldTypes){
int nbFailed=0;
StringBuilder builder = null;
for(int i =0 ; i < dfn.getColumnCount() ; i++){
if(dfn.getColumnType(i)!=found.getColumnType(matcher.getMatchForA(i))){
if(nbFailed==0){
builder=new StringBuilder();
builder.append("The following field(s) in table " + dfn.getName() + " had the wrong type: ");
}else{
builder.append(", ");
}
builder.append(dfn.getColumnName(i));
nbFailed++;
}
}
if(nbFailed>0){
if(report!=null){
report.log(builder.toString());
}
return false;
}
}
return true;
}
@Override
public <T extends ODLTableReadOnly> T adaptToTableUsingNames(ODLDatastore<? extends T> ds, String fromTable, ODLTableDefinition toTable, ExecutionReport report) {
AdapterConfig adapterConfig = new AdapterConfig();
AdapterConfig.addSameNameTable(toTable, adapterConfig);
adapterConfig.getTable(0).setFromTable(fromTable);
ODLDatastore<?extends T> ret=AdapterBuilderUtils.createSimpleAdapter(ds, adapterConfig, report);
if(report.isFailed()){
return null;
}
return ret.getTableAt(0);
}
}