/*******************************************************************************
* 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.map.hash.TIntObjectHashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.opendoorlogistics.api.Func;
import com.opendoorlogistics.api.Tables;
import com.opendoorlogistics.api.components.PredefinedTags;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLDatastoreAlterable;
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.TableQuery;
import com.opendoorlogistics.api.tables.TableQuery.SpatialTableQuery;
import com.opendoorlogistics.core.api.impl.ODLApiImpl;
import com.opendoorlogistics.core.formulae.Function;
import com.opendoorlogistics.core.formulae.FunctionParameters;
import com.opendoorlogistics.core.formulae.Functions;
import com.opendoorlogistics.core.formulae.Functions.FmConst;
import com.opendoorlogistics.core.geometry.ODLGeomImpl;
import com.opendoorlogistics.core.geometry.SpatialTableQueryImpl;
import com.opendoorlogistics.core.geometry.functions.FmLatitude;
import com.opendoorlogistics.core.geometry.functions.FmLongitude;
import com.opendoorlogistics.core.geometry.operations.OneByOneSpatialQuery;
import com.opendoorlogistics.core.scripts.formulae.FmLocalElement;
import com.opendoorlogistics.core.scripts.formulae.TableParameters;
import com.opendoorlogistics.core.scripts.formulae.TableParameters.TableFetcher;
import com.opendoorlogistics.core.scripts.wizard.TagUtils;
import com.opendoorlogistics.core.tables.ColumnValueProcessor;
import com.opendoorlogistics.core.tables.ODLRow;
import com.opendoorlogistics.core.tables.ODLRowReadOnly;
import com.opendoorlogistics.core.tables.memory.ODLDatastoreImpl;
import com.opendoorlogistics.core.tables.utils.TableFlagUtils;
import com.opendoorlogistics.core.tables.utils.TableUtils;
import com.sun.corba.se.impl.oa.toa.TOA;
/**
* An AdaptedDecorator uses a mapping to turn one set of tables into another
*
* @author Phil
*
*/
final public class AdaptedDecorator<T extends ODLTableDefinition> extends AbstractDecorator<T> {
private final AdapterMapping mapping;
private final List<ODLDatastore<? extends T>> sources;
public static class AdapterMapping {
private final ODLDatastore<? extends ODLTableDefinition> outputDs;
private final boolean destinationDsIsCopy;
private TIntObjectHashMap<MappedTable> mappedByDestTableId = new TIntObjectHashMap<>();
public AdapterMapping(ODLDatastore<? extends ODLTableDefinition> destinationModel) {
this(destinationModel, false);
}
public AdapterMapping(ODLDatastore<? extends ODLTableDefinition> destinationModel, boolean createAlterableCopyOfDestinationDfn) {
if(createAlterableCopyOfDestinationDfn){
// take copy so we can add to it
ODLApiImpl api = new ODLApiImpl();
ODLDatastoreAlterable<? extends ODLTableAlterable> copy = api.tables().createAlterableDs();
for(int i =0 ; i < destinationModel.getTableCount();i++){
api.tables().copyTableDefinition(destinationModel.getTableAt(i), copy);
}
outputDs = copy;
destinationDsIsCopy = true;
}else{
outputDs = destinationModel;
destinationDsIsCopy = false;
}
}
public static class MappedTable {
private int sourceDataSourceIndx = 0;
private int sourceTableId;
private final List<MappedField> fields = new ArrayList<>();
public int getSourceDataSourceIndx() {
return sourceDataSourceIndx;
}
public void setSourceDataSourceIndx(int sourceDataSourceIndx) {
this.sourceDataSourceIndx = sourceDataSourceIndx;
}
public int getSourceTableId() {
return sourceTableId;
}
public void setSourceTableId(int sourceTableId) {
this.sourceTableId = sourceTableId;
}
public List<MappedField> getFields() {
return fields;
}
}
public static class MappedField {
private int sourceColumnIndex;
private Function formula;
public int getSourceColumnIndex() {
return sourceColumnIndex;
}
public void setSourceColumnIndex(int sourceColumnIndex) {
this.sourceColumnIndex = sourceColumnIndex;
}
public Function getFormula() {
return formula;
}
public void setFormula(Function formula) {
this.formula = formula;
}
}
public static AdapterMapping createUnassignedMapping(ODLDatastore<? extends ODLTableDefinition> dm) {
return createUnassignedMapping(dm,false);
}
public static AdapterMapping createUnassignedMapping(ODLDatastore<? extends ODLTableDefinition> dm, boolean alterableDestination) {
AdapterMapping ret = new AdapterMapping(dm, alterableDestination);
for (int i = 0; i < dm.getTableCount(); i++) {
// create mapped table record
ODLTableDefinition tm = dm.getTableAt(i);
allocateTable(tm, ret);
}
return ret;
}
public static AdapterMapping createUnassignedMapping(ODLTableDefinition table, boolean alterableDestination) {
// create dummy datastore with just the table
ODLDatastoreImpl<ODLTableDefinition> dummy = new ODLDatastoreImpl<>(null);
dummy.addTable(table);
AdapterMapping ret = new AdapterMapping(dummy, alterableDestination);
allocateTable(table, ret);
return ret;
}
public void addMappedTable(MappedTable table, int destinationTableId) {
mappedByDestTableId.put(destinationTableId, table);
}
private static void allocateTable(ODLTableDefinition table, AdapterMapping mapping) {
MappedTable mappedTable = new MappedTable();
mappedTable.sourceTableId = -1;
mapping.mappedByDestTableId.put(table.getImmutableId(), mappedTable);
int nbFields = table.getColumnCount();
for (int j = 0; j < nbFields; j++) {
MappedField mf = new MappedField();
mf.sourceColumnIndex = -1;
mappedTable.fields.add(mf);
}
}
public void setTableSourceId(int destinationTableId, int sourceDatastoreIndx, int sourceTableId) {
mappedByDestTableId.get(destinationTableId).sourceDataSourceIndx = sourceDatastoreIndx;
mappedByDestTableId.get(destinationTableId).sourceTableId = sourceTableId;
}
public void addMappedFormula(int destinationTableId, String name,ODLColumnType type, Function function){
addField(destinationTableId, name, type, -1,function);
}
public void addMappedField(int destinationTableId, String name,ODLColumnType type, int sourceFieldIndx){
addField(destinationTableId, name, type, sourceFieldIndx,null);
}
private void addField(int destinationTableId, String name, ODLColumnType type, int sourceFieldIndx, Function function) {
MappedTable mappedTable = mappedByDestTableId.get(destinationTableId);
// add to definition
if(!destinationDsIsCopy){
throw new UnsupportedOperationException();
}
ODLTableDefinitionAlterable dfn = (ODLTableDefinitionAlterable)outputDs.getTableByImmutableId(destinationTableId);
dfn.addColumn(-1, name, type, 0);
// add to mapping
MappedField mappedField = new MappedField();
mappedTable.fields.add(mappedField);
mappedField.sourceColumnIndex = sourceFieldIndx;
mappedField.formula = function;
}
public void setFieldSourceIndx(int destinationTableId, int destinationFieldIndx, int sourceFieldIndx) {
mappedByDestTableId.get(destinationTableId).fields.get(destinationFieldIndx).sourceColumnIndex = sourceFieldIndx;
}
public void setFieldFormula(int destinationTableId, int destinationFieldIndx, Function calc) {
mappedByDestTableId.get(destinationTableId).fields.get(destinationFieldIndx).formula = calc;
}
public int getSourceDatasourceIndx(int destinationTableId) {
return mappedByDestTableId.get(destinationTableId).sourceDataSourceIndx;
}
public int getSourceTableId(int destinationTableId) {
return mappedByDestTableId.get(destinationTableId).sourceTableId;
}
public int getSourceColumnIndx(int destinationTableId, int destinationColIndx) {
return mappedByDestTableId.get(destinationTableId).fields.get(destinationColIndx).sourceColumnIndex;
}
public Function getFieldFormula(int destinationTableId, int destinationColIndx) {
return mappedByDestTableId.get(destinationTableId).fields.get(destinationColIndx).formula;
}
public int getFieldCount(int destinationTableId){
return mappedByDestTableId.get(destinationTableId).fields.size();
}
public ODLDatastore<? extends ODLTableDefinition> getDestinationModel() {
return outputDs;
}
}
public AdaptedDecorator(AdapterMapping mapping, T table) {
this(mapping, wrapInDs(table));
}
private static <T extends ODLTableDefinition> ODLDatastore<? extends T> wrapInDs(T table){
ODLDatastoreImpl< T> tmpDs = new ODLDatastoreImpl<T>(null);
tmpDs.addTable(table);
return tmpDs;
}
public AdaptedDecorator(AdapterMapping mapping, ODLDatastore<? extends T> source) {
this.mapping = mapping;
this.sources = new ArrayList<ODLDatastore<? extends T>>(1);
this.sources.add(source);
}
public AdaptedDecorator(AdapterMapping mapping, List<ODLDatastore<? extends T>> sources) {
this.mapping = mapping;
this.sources = sources;
}
@Override
public int getTableCount() {
return mapping.getDestinationModel().getTableCount();
}
@Override
public T getTableAt(final int i) {
return getTableByImmutableId(mapping.getDestinationModel().getTableAt(i).getImmutableId());
}
@Override
public String toString() {
return TableUtils.convertToString(this);
}
@Override
public void addListener(ODLListener tml, int... tableIds) {
}
@Override
public void removeListener(ODLListener tml) {
}
private T sourceTable(int destinationTableId) {
int ds = mapping.getSourceDatasourceIndx(destinationTableId);
int srcId = mapping.getSourceTableId(destinationTableId);
if (ds != -1 && srcId != -1) {
return sources.get(ds).getTableByImmutableId(srcId);
}
return null;
}
@Override
public int getRowCount(int tableId) {
T src = sourceTable(tableId);
if (src != null) {
return ((ODLTableReadOnly) src).getRowCount();
}
return 0;
}
@Override
public long getRowGlobalId(int tableId, int rowIndex) {
T src = sourceTable(tableId);
if (src != null) {
return ((ODLTableReadOnly) src).getRowId(rowIndex);
}
return -1;
}
// @Override
// protected int getRowIndexByGlobalId(int tableId, long immutableId) {
// T src = sourceTable(tableId);
// if(src!=null){
// return ((ODLTableReadOnly)src).getRowIndexByGlobalId(immutableId);
// }
// return -1;
// }
//
// @Override
// protected int getRowIndexByLocalId(int tableId, int localId) {
// T src = sourceTable(tableId);
// if(src!=null){
// return ((ODLTableReadOnly)src).getRowIndexByLocalId(localId);
// }
// return -1;
// }
@Override
public boolean containsRowId(int tableId, long rowId) {
ODLTableReadOnly src = (ODLTableReadOnly) sourceTable(tableId);
if (src == null) {
return false;
}
return src.containsRowId(rowId);
}
@Override
public Object getValueById(int tableId, long rowId, int columnIndex) {
return getValue(tableId, rowId, -1, columnIndex);
}
/**
* Get value by row index or row id (whichever is available). Formula fields will always work by id however, and will fetch the id from the row
* index (and hence assume id to be unique in a table - which unions can violate).
*
* @param destinationTableId
* @param rowId
* @param rowIndex
* @param columnIndex
* @return
*/
private Object getValue(final int destinationTableId,final long originalRowId,final int rowIndex,final int columnIndex) {
TableFetcher tableFetcher = TableParameters.createTableFetcher(sources);
return getValue(destinationTableId, originalRowId, rowIndex, columnIndex, tableFetcher);
}
private Object getValue(final int destinationTableId, final long originalRowId, final int rowIndex, final int columnIndex,
TableFetcher srcTableFetcher) {
ODLTableReadOnly srcTable=null;
int ds = mapping.getSourceDatasourceIndx(destinationTableId);
int srcId = mapping.getSourceTableId(destinationTableId);
if (ds != -1 && srcId != -1) {
srcTable = srcTableFetcher.getTableById(ds, srcId);
}
if(srcTable==null){
return null;
}
int srcCol = mapping.getSourceColumnIndx(destinationTableId, columnIndex);
Function formula = mapping.getFieldFormula(destinationTableId, columnIndex);
Object ret = null;
ODLTableDefinition destTable = mapping.getDestinationModel().getTableByImmutableId(destinationTableId);
long rowId = originalRowId;
if (formula != null) {
// we need to use the rowid... so get it
if (rowId == -1) {
rowId = ((ODLTableReadOnly) srcTable).getRowId(rowIndex);
}
// create the 'this row' object so formula can reference other formula in the same table adapter
ODLRowReadOnly thisRow = new ODLRowReadOnly() {
@Override
public int getRowIndex() {
throw new UnsupportedOperationException();
}
@Override
public ODLTableDefinition getDefinition() {
throw new UnsupportedOperationException();
}
@Override
public int getColumnCount() {
throw new UnsupportedOperationException();
}
@Override
public Object get(int col) {
return getValue(destinationTableId, originalRowId, rowIndex, col);
}
};
FunctionParameters parameters = new TableParameters(srcTableFetcher, mapping.getSourceDatasourceIndx(destinationTableId), mapping.getSourceTableId(destinationTableId), rowId, rowIndex, thisRow);
ret = formula.execute(parameters);
if (ret == Functions.EXECUTION_ERROR) {
ret = null;
}
// ensure correct type
if (ret != null) {
ret = getConvertedType(ret, null, destTable, columnIndex);
}
} else if (srcTable != null && srcCol != -1) {
if (rowId != -1) {
ret = ((ODLTableReadOnly) srcTable).getValueById(rowId, srcCol);
} else if (rowIndex != -1) {
ret = ((ODLTableReadOnly) srcTable).getValueAt(rowIndex, srcCol);
}
if (ret != null) {
// convert types. original type is known in this case (which helps the conversion)
ret = getConvertedType(ret, srcTable.getColumnType(srcCol), destTable, columnIndex);
}
}
return ret;
}
private Object getConvertedType(Object original, ODLColumnType srcColumnType, ODLTableDefinition destTable, int destCol) {
ODLColumnType destColType = destTable.getColumnType(destCol);
if (original == null) {
// may return a default value
return ColumnValueProcessor.convertToMe(destColType, null);
}
// check for converting geometry to a lat or long
if ((srcColumnType == ODLColumnType.GEOM || ODLGeomImpl.class.isInstance(original)) && destColType == ODLColumnType.DOUBLE) {
if (TagUtils.hasTag(PredefinedTags.LATITUDE, destTable, destCol)) {
return new FmLatitude(new FmConst(original)).execute(null);
} else if (TagUtils.hasTag(PredefinedTags.LONGITUDE, destTable, destCol)) {
return new FmLongitude(new FmConst(original)).execute(null);
}
}
if (srcColumnType != null) {
return ColumnValueProcessor.convertToMe(destColType, original, srcColumnType);
}
return ColumnValueProcessor.convertToMe(destColType, original);
}
@Override
public Object getValueAt(int tableId, int rowIndex, int columnIndex) {
return getValue(tableId, -1, rowIndex, columnIndex);
}
@Override
public ODLColumnType getColumnFieldType(int tableId, int col) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnType(col);
}
@Override
public String getColumnName(int tableId, int col) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnName(col);
}
@Override
public int getColumnCount(int tableId) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnCount();
}
@Override
public String getName(int tableId) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getName();
}
@Override
public long getFlags(int tableId) {
long destFlags= mapping.getDestinationModel().getTableByImmutableId(tableId).getFlags();
long ret = destFlags;
T src = sourceTable(tableId);
if (src != null) {
// always pass the global datastore flag
// ret |= src.getFlags() & DefinedFlags.FLAG_IS_FROM_GLOBAL_DATASTORE;
// the source sets the permission flags
ret = TableFlagUtils.removeFlags(ret, TableFlags.UI_EDIT_PERMISSION_FLAGS);
long srcFlags = src.getFlags();
ret |= TableFlagUtils.addFlags(ret, TableFlags.UI_EDIT_PERMISSION_FLAGS & srcFlags);
}
// turn off move for the moment (no sort)
ret = TableFlagUtils.removeFlags(ret, TableFlags.UI_MOVE_ALLOWED);
return ret;
}
@Override
public long getColumnFlags(int tableId, int col) {
long ret = mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnFlags(col);
if (mapping.getFieldFormula(tableId, col) != null) {
// ensure calculated columns are read-only as far as UI is concerned
ret |= TableFlags.FLAG_IS_READ_ONLY;
}
return ret;
}
@Override
public int getColumnImmutableId(int tableId, int col) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnImmutableId(col);
}
@Override
public String getColumnDescription(int tableId, int col) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnDescription(col);
}
@Override
public java.util.Set<String> getColumnTags(int tableId, int col) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnTags(col);
}
@Override
public java.util.Set<String> getTags(int tableId) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getTags();
}
@Override
public void setColumnDescription(int tableId, int col, String description) {
throw new UnsupportedOperationException();
}
@Override
public void setValueById(int tableId, Object aValue, long rowId, int columnIndex) {
setValue(tableId, aValue, rowId, -1, columnIndex);
}
/**
* Set value using either the row id or row index (whichever is available).
*
* @param tableId
* @param aValue
* @param rowId
* @param rowIndex
* @param col
*/
private void setValue(int tableId, Object aValue, long rowId, int rowIndex, int col) {
T decoratedTable = sourceTable(tableId);
if (decoratedTable == null) {
return;
}
// Get the source
int decoratedCol = mapping.getSourceColumnIndx(tableId, col);
Function formula = mapping.getFieldFormula(tableId, col);
// If this is a formula which just points to the original element, then let us still write back to it
if(formula!=null && formula.getClass() == FmLocalElement.class){
decoratedCol = ((FmLocalElement)formula).getColumnIndex();
formula = null;
}
if (decoratedTable != null && decoratedCol != -1 && formula == null) {
ODLColumnType decColType = decoratedTable.getColumnType(decoratedCol);
if (aValue != null) {
aValue = ColumnValueProcessor.convertToMe(decColType, aValue);
}
if (rowId != -1) {
((ODLTable) decoratedTable).setValueById(aValue, rowId, decoratedCol);
} else if (rowIndex != -1) {
((ODLTable) decoratedTable).setValueAt(aValue, rowIndex, decoratedCol);
}
}
}
@Override
public void setValueAt(int tableId, Object aValue, int rowIndex, int columnIndex) {
setValue(tableId, aValue, -1, rowIndex, columnIndex);
}
@Override
public int createEmptyRow(int tableId, long rowId) {
T src = sourceTable(tableId);
if (src != null) {
return ((ODLTable) src).createEmptyRow(rowId);
}
return -1;
}
@Override
public void insertEmptyRow(int tableId, int insertAtRowNb, long rowId) {
T src = sourceTable(tableId);
if (src != null) {
((ODLTable) src).insertEmptyRow(insertAtRowNb, rowId);
}
}
@Override
public void deleteRow(int tableId, int rowNumber) {
T src = sourceTable(tableId);
if (src != null) {
((ODLTable) src).deleteRow(rowNumber);
}
}
@Override
public int addColumn(int tableId, int colId, String name, ODLColumnType type, long flags) {
// not supported for a mapped database
throw new UnsupportedOperationException();
}
@Override
public void setFlags(int tableId, long flags) {
// not supported for a mapped database
throw new UnsupportedOperationException();
}
@Override
public void setColumnFlags(int tableId, int col, long flags) {
// not supported for a mapped database
throw new UnsupportedOperationException();
}
@Override
public void deleteCol(int tableId, int col) {
throw new RuntimeException();
}
@Override
public boolean insertCol(int tableId, int colId, int col, String name, ODLColumnType type, long flags, boolean allowDuplicateNames) {
throw new RuntimeException();
}
@Override
public void disableListeners() {
}
@Override
public void enableListeners() {
}
@Override
public T createTable(String tablename, int id) {
throw new RuntimeException();
}
@Override
public void deleteTableById(int tableId) {
throw new RuntimeException();
}
@Override
public void startTransaction() {
new MultiDsTransactions<T>().startTransaction(sources);
}
@Override
public void endTransaction() {
new MultiDsTransactions<T>().endTransaction(sources);
}
@Override
public boolean isInTransaction() {
return new MultiDsTransactions<T>().isInTransaction(sources);
}
@Override
public boolean setTableName(int tableId, String newName) {
return false;
}
@Override
public long getFlags() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setFlags(long flags) {
throw new UnsupportedOperationException();
}
@Override
public ODLDatastoreAlterable<T> deepCopyWithShallowValueCopy(boolean lazyCopy) {
throw new UnsupportedOperationException();
}
@Override
public void setColumnTags(int tableId, int col, Set<String> tags) {
throw new UnsupportedOperationException();
}
@Override
public void setTags(int tableId, Set<String> tags) {
throw new UnsupportedOperationException();
}
@Override
public Object getColumnDefaultValue(int tableId, int col) {
return mapping.getDestinationModel().getTableByImmutableId(tableId).getColumnDefaultValue(col);
}
@Override
public void setColumnDefaultValue(int tableId, int col, Object value) {
throw new UnsupportedOperationException();
}
@Override
public long[] find(int tableId, int col, Object value) {
T src = sourceTable(tableId);
if (src == null) {
return null;
}
int srcCol = mapping.getSourceColumnIndx(tableId, col);
Function formula = mapping.getFieldFormula(tableId, col);
if (formula != null || srcCol == -1) {
// cannot use index on calculated column
return TableUtils.find((ODLTableReadOnly) getTableByImmutableId(tableId), col, value);
} else {
return ((ODLTableReadOnly) src).find(srcCol, value);
}
}
@Override
public ODLTableReadOnly query(int tableId, TableQuery query) {
class RemapHelper{
int remapCol(int destCol){
if(destCol==-1){
return -1;
}
int srcCol = mapping.getSourceColumnIndx(tableId, destCol);
Function formula = mapping.getFieldFormula(tableId, destCol);
if(formula==null){
return srcCol;
}
return -1;
}
}
RemapHelper remap = new RemapHelper();
ODLTableReadOnly src =(ODLTableReadOnly) sourceTable(tableId);
if (src == null) {
return null;
}
ODLTableDefinition outDfn = getTableByImmutableId(tableId);
if(outDfn==null){
return null;
}
// Try passing the query down to the decorated table...
ODLTableReadOnly srcQuery=null;
if(query instanceof SpatialTableQuery){
// Remap the columns. If the latitude, longitude or geom columns
// come from functions then the query will need to be executed in this adapter.
// If either lat/long columns OR geom column are currently set and one or the other exist
// (are not functions) in the raw table, we pass the query down (to hopefully use a spatial lookup - e.g. quadtree).
// Otherwise we execute it directly on the adapted table, examining each row one-by-one
SpatialTableQueryImpl remapped = new SpatialTableQueryImpl((SpatialTableQuery)query);
remapped.setLatitudeColumn(remap.remapCol(remapped.getLatitudeColumn()));
remapped.setLongitudeColumn(remap.remapCol(remapped.getLongitudeColumn()));
remapped.setGeomColumn(remap.remapCol(remapped.getGeomColumn()));
if((remapped.getLatitudeColumn()!=-1 && remapped.getLongitudeColumn()!=-1) || remapped.getGeomColumn()!=-1){
srcQuery = src.query(remapped);
if(srcQuery==null){
return null;
}
}
}
// If we didn't pass the query down the decorator then do it now
ODLApiImpl api = new ODLApiImpl();
if(srcQuery==null){
if(query instanceof SpatialTableQuery){
srcQuery = new OneByOneSpatialQuery(api).query(src, (SpatialTableQuery)query);
}else{
throw new UnsupportedOperationException("Unsupported query type");
}
}
// Create a table fetcher which returns our query result table instead
int srcDsIndx = mapping.getSourceDatasourceIndx(tableId);
int srcTableId = mapping.getSourceTableId(tableId);
TableFetcher defaultFetcher = TableParameters.createTableFetcher(sources);
final ODLTableReadOnly finalSrcQuery = srcQuery;
TableFetcher fetcher = new TableFetcher() {
@Override
public ODLTableReadOnly getTableById(int datastoreIndx, int tableId) {
if(datastoreIndx == srcDsIndx && tableId == srcTableId){
return finalSrcQuery;
}
return defaultFetcher.getTableById(datastoreIndx, srcTableId);
}
};
// Copy the results over
Tables tables = api.tables();
ODLDatastoreAlterable<? extends ODLTableAlterable > ds = tables.createAlterableDs();
ODLTableAlterable ret=(ODLTableAlterable)tables.copyTableDefinition(outDfn, ds);
int nQueryResultRow = srcQuery.getRowCount();
int nDestCol = outDfn.getColumnCount();
for(int queryResultRow = 0; queryResultRow < nQueryResultRow ; queryResultRow++){
int outRow = ret.createEmptyRow(srcQuery.getRowId(queryResultRow));
for(int destCol = 0 ; destCol < nDestCol ; destCol++){
Object value = getValue(tableId, -1, queryResultRow, destCol, fetcher);
ret.setValueAt(value, outRow, destCol);
}
}
return ret;
}
@Override
public long getRowFlags(int tableId, long rowId) {
T src = sourceTable(tableId);
if (src != null) {
return ((ODLTableReadOnly) src).getRowFlags(rowId);
}
return 0;
}
@Override
public void setRowFlags(int tableId, long flags, long rowId) {
T src = sourceTable(tableId);
if (src != null) {
((ODLTable) src).setRowFlags(flags, rowId);
}
}
@Override
public void rollbackTransaction() {
new MultiDsTransactions<T>().rollbackTransaction(sources);
}
@Override
public boolean isRollbackSupported() {
return new MultiDsTransactions<T>().isRollbackSupported(sources);
}
@Override
public boolean getTableExists(int tableId) {
return true;
}
@Override
public ODLTableDefinition deepCopyWithShallowValueCopy(int tableId) {
throw new UnsupportedOperationException();
}
@Override
public long getRowLastModifiedTimeMillisecs(int tableId, long rowId) {
T src = sourceTable(tableId);
if (src != null) {
return ((ODLTableReadOnly) src).getRowLastModifiedTimeMillsecs(rowId);
}
return -1;
}
}