/*******************************************************************************
* 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.scripts.elements;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastoreAlterable;
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.api.impl.ODLApiImpl;
import com.opendoorlogistics.core.scripts.elements.AdapterColumnConfig.SortField;
import com.opendoorlogistics.core.tables.memory.ODLAbstractTableDefinition;
import com.opendoorlogistics.core.tables.memory.ODLColumnDefinition;
import com.opendoorlogistics.core.tables.utils.DatastoreComparer;
import com.opendoorlogistics.core.tables.utils.DatastoreCopier;
import com.opendoorlogistics.core.tables.utils.HasShortDescription;
import com.opendoorlogistics.core.utils.JAXBUtils;
import gnu.trove.set.hash.TIntHashSet;
@XmlRootElement(name = "AdaptedTable")
final public class AdaptedTableConfig extends ODLAbstractTableDefinition<AdapterColumnConfig> implements Serializable, HasShortDescription{
/**
*
*/
private static final long serialVersionUID = -8953723014361990024L;
@XmlTransient
private String fromTable;
@XmlTransient
private String fromDatastore;
@XmlTransient
private String joinTable;
@XmlTransient
private String joinDatastore;
@XmlTransient
private boolean join;
@XmlTransient
private String filterFormula="";
@XmlTransient
private boolean limitResults=false;
@XmlTransient
private int maxNbRows=1000;
@XmlTransient
private List<UserFormula> userFormulae = new ArrayList<UserFormula>();
@XmlTransient
private String shortEditorUINote;
@XmlTransient
private boolean fetchSourceFields=false;
@XmlTransient
private ArrayList<EmbeddedDataRow> data;
public AdaptedTableConfig(){}
public AdapterColumnConfig addMappedFormulaColumn(String formula, String to, ODLColumnType toType, long toFlags){
AdapterColumnConfig ret = new AdapterColumnConfig(-1, null, to, toType, toFlags);
ret.setUseFormula(true);
ret.setFormula(formula);
columns.add(ret);
return ret;
}
public AdapterColumnConfig addMappedColumn(String from, String to, ODLColumnType toType, long toFlags){
AdapterColumnConfig ret = new AdapterColumnConfig(nextColumnId(),from,to, toType, toFlags);
columns.add(ret);
return ret;
}
/**
* Create a copy but without any columns
* @return
*/
public AdaptedTableConfig createNoColumnsCopy(){
AdaptedTableConfig ret = new AdaptedTableConfig();
ret.setName(getName());
ret.setFrom(getFromDatastore(), getFromTable());
ret.setFilterFormula(getFilterFormula());
ret.setFlags(getFlags());
ret.setTags(getTags());
ret.setLimitResults(isLimitResults());
ret.setMaxNumberRows(maxNbRows);
ret.setJoin(isJoin());
ret.setJoinDatastore(getJoinDatastore());
ret.setJoinTable(getJoinTable());
ret.setFetchSourceFields(isFetchSourceFields());
if(userFormulae!=null){
ret.userFormulae = new ArrayList<UserFormula>();
for(UserFormula uf : userFormulae){
ret.userFormulae.add(new UserFormula(uf));
}
}
return ret;
}
public AdaptedTableConfig deepCopy(){
AdaptedTableConfig ret = createNoColumnsCopy();
for(AdapterColumnConfig column: getColumns()){
ret.getColumns().add( new AdapterColumnConfig(column, column.getImmutableId()));
}
if(data!=null){
ret.data = new ArrayList<>(data.size());
for(EmbeddedDataRow row:data){
if(row!=null){
// strings are immutable so just need to copy the collection
ret.data.add(new EmbeddedDataRow(row.getValues()));
}else{
ret.data.add(null);
}
}
}
return ret;
}
public void addMappedColumn(AdapterColumnConfig config){
for(ODLColumnDefinition col:columns){
if(config.getImmutableId()==col.getImmutableId()){
throw new RuntimeException();
}
}
columns.add(config);
}
public String getFromTable() {
return fromTable;
}
@XmlAttribute
public void setFromTable(String fromTable) {
this.fromTable = fromTable;
}
public String getJoinTable() {
return joinTable;
}
@XmlAttribute
public void setJoinTable(String table) {
this.joinTable = table;
}
@Override
public String toString() {
return JAXBUtils.toXMLString(this);
}
public String getFromDatastore() {
return fromDatastore;
}
@XmlAttribute
public void setFromDatastore(String fromDataSourceId) {
this.fromDatastore = fromDataSourceId;
}
public String getJoinDatastore() {
return joinDatastore;
}
@XmlAttribute
public void setJoinDatastore(String dataSourceId) {
this.joinDatastore = dataSourceId;
}
public void setFrom(String datastore, String table){
setFromDatastore(datastore);
setFromTable(table);
}
@Override
protected AdapterColumnConfig createColObj(int id, String name, ODLColumnType type, long flags) {
id = validateNewColumnId(id);
return new AdapterColumnConfig(id,null, name, type, flags);
}
public void moveColumnUp(int indx){
if(indx>0){
// don't use the datastore modifier class as this copied the column and won't copy the FROM field
AdapterColumnConfig field = columns.remove(indx);
columns.add(indx-1, field);
}
}
public void moveColumnDown(int indx){
if(indx < getColumnCount()-1){
// don't use the datastore modifier class as this copied the column and won't copy the FROM field
AdapterColumnConfig field = columns.remove(indx);
columns.add(indx+1, field);
}
}
public AdapterColumnConfig getColumn(int i){
return columns.get(i);
}
@Override
public String getShortDescription() {
return getTableDescription(true);
}
String getTableDescription(boolean isStandalone) {
String from = isStandalone ? "Table" : "table";
from += " '"+ getFromDatastore()+ "'." ;
if(getFromTable()!=null){
from += "'" + getFromTable() + "'";
}
return from + " \u27A1 " + " to table '" +getName() + "'";
}
/**
* Used by JAXB
* @param columns
*/
@XmlElement(name = "AdaptedColumn")
void setColumns(List<AdapterColumnConfig> columns){
this.columns = columns;
}
/**
* Used by JAXB. Must return a list or jaxb
* binding fails
* @return
*/
public List<AdapterColumnConfig> getColumns(){
return columns;
}
public String getFilterFormula() {
return filterFormula;
}
@XmlElement(name = "FilterFormula")
public void setFilterFormula(String filterFormula) {
this.filterFormula = filterFormula;
}
public ODLTableDefinitionAlterable createOutputDefinition(){
ODLTableDefinitionAlterable ret = new ODLApiImpl().tables().createAlterableTable(getName());
createOutputDefinition(ret);
return ret;
}
public void createOutputDefinition(ODLTableDefinitionAlterable ret){
if(ret.getColumnCount()!=0){
throw new RuntimeException();
}
DatastoreCopier.copyTableDefinition(this, ret,false);
// remove sort columns from the definition
TIntHashSet sortCols = new TIntHashSet();
int n = getColumnCount();
for(int i =0 ; i<n;i++){
if(getColumn(i).getSortField() != SortField.NO){
sortCols.add(i);
}
}
for(int i = n-1;i>=0 ; i--){
if(sortCols.contains(i)){
ret.deleteColumn(i);
}
}
}
@Override
public Object getColumnDefaultValue(int col) {
// Adapted tables have default values supplied by setting a formula.
// The method can still be called however...
return null;
}
@Override
public void setColumnDefaultValue(int col, Object value) {
// Adapted tables have default values supplied by setting a formula.
// The method can still be called however...
}
public boolean isLimitResults() {
return limitResults;
}
@XmlAttribute(name ="IsLimitedResults")
public void setLimitResults(boolean limitResults) {
this.limitResults = limitResults;
}
public int getMaxNumberRows() {
return maxNbRows;
}
@XmlAttribute(name ="MaxNumberRows")
public void setMaxNumberRows(int resultsLimit) {
this.maxNbRows = resultsLimit;
}
@Override
public ODLTableDefinition deepCopyWithShallowValueCopy() {
throw new UnsupportedOperationException();
}
/**
* Used by JAXB
* @param columns
*/
@XmlElement(name = "UserFormulae")
public void setUserFormulae(List<UserFormula> userFormulae){
this.userFormulae = userFormulae;
}
/**
* Used by JAXB. Must return a list or jaxb
* binding fails
* @return
*/
public List<UserFormula> getUserFormulae(){
return userFormulae;
}
public String getShortEditorUINote() {
return shortEditorUINote;
}
@XmlAttribute(name ="ShortEditorUINote")
public void setShortEditorUINote(String shortEditorUINote) {
this.shortEditorUINote = shortEditorUINote;
}
public boolean isJoin() {
return join;
}
@XmlAttribute(name ="Join")
public void setJoin(boolean join) {
this.join = join;
}
public boolean isFetchSourceFields() {
return fetchSourceFields;
}
@XmlAttribute(name ="FetchSourceFields")
public void setFetchSourceFields(boolean fetchSourceFields) {
this.fetchSourceFields = fetchSourceFields;
}
public ArrayList<EmbeddedDataRow> getData() {
return data;
}
@XmlElement(name = "Data")
public void setData(ArrayList<EmbeddedDataRow> data) {
this.data = data;
}
/**
* Set the data table, converting the input table to list-of-strings representation.
* The table must match the adapter's definition or an exception is thrown
* @param table
*/
@XmlTransient
public void setDataTable(ODLTableReadOnly table){
if(!DatastoreComparer.isSameStructure(table, createOutputDefinition(),0)){
throw new IllegalArgumentException();
}
int nr = table.getRowCount();
int nc = table.getColumnCount();
data = new ArrayList<>(nr);
ODLApi api = new ODLApiImpl();
for(int row=0; row<nr; row++){
EmbeddedDataRow newRow = new EmbeddedDataRow(nc);
data.add(newRow);
for(int col=0; col < nc ; col++){
newRow.getValues().add(api.values().canonicalStringRepresentation(table.getValueAt(row, col)));
}
}
}
/**
* Create the data table from this adapter. The data is only filled if there
* is row data set on the adapter.
* @return
*/
@XmlTransient
public ODLTableAlterable getDataTable(){
ODLApi api = new ODLApiImpl();
ODLTableAlterable ret = api.tables().createAlterableTable(getName());
getDataTable(ret);
ret.setFlags(ret.getFlags() | TableFlags.UI_SET_INSERT_DELETE_PERMISSION_FLAGS);
return ret;
}
private void getDataTable(ODLTableAlterable ret) {
createOutputDefinition(ret);
int nc = ret.getColumnCount();
if(data!=null){
for(EmbeddedDataRow row:data){
if(row!=null){
int rowNb = ret.createEmptyRow(-1);
int len = row.getValues().size();
len = Math.min(len, nc);
for(int col =0 ; col < len ;col++){
ret.setValueAt(row.getValues().get(col), rowNb, col);
}
}
}
}
}
public ODLTableAlterable getDataTable(ODLDatastoreAlterable<? extends ODLTableAlterable> ds){
ODLTableAlterable ret = ds.createTable(getName(), -1);
getDataTable(ret);
return ret;
}
}