/*******************************************************************************
* 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.studio.components.tables;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.Tables;
import com.opendoorlogistics.api.components.ComponentConfigurationEditorAPI;
import com.opendoorlogistics.api.components.ComponentControlLauncherApi;
import com.opendoorlogistics.api.components.ComponentControlLauncherApi.ControlLauncherCallback;
import com.opendoorlogistics.api.components.ComponentExecutionApi;
import com.opendoorlogistics.api.components.ODLComponent;
import com.opendoorlogistics.api.components.PredefinedTags;
import com.opendoorlogistics.api.scripts.ScriptAdapter;
import com.opendoorlogistics.api.scripts.ScriptInputTables;
import com.opendoorlogistics.api.scripts.ScriptOption;
import com.opendoorlogistics.api.scripts.ScriptOption.OutputType;
import com.opendoorlogistics.api.scripts.ScriptTemplatesBuilder;
import com.opendoorlogistics.api.scripts.ScriptTemplatesBuilder.BuildScriptCallback;
import com.opendoorlogistics.api.standardcomponents.TableViewer;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLDatastoreAlterable;
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.tables.decorators.datastores.AdaptedDecorator;
import com.opendoorlogistics.core.tables.decorators.datastores.AdaptedDecorator.AdapterMapping;
import com.opendoorlogistics.core.tables.memory.ODLDatastoreImpl;
import com.opendoorlogistics.core.tables.utils.TableUtils;
import com.opendoorlogistics.core.utils.Colours;
import com.opendoorlogistics.studio.internalframes.HasInternalFrames;
import com.opendoorlogistics.studio.panels.TableViewerPanel;
import com.opendoorlogistics.studio.tables.grid.GridEditPermissions;
import com.opendoorlogistics.studio.tables.grid.adapter.RowStyler;
import com.opendoorlogistics.utils.ui.Icons;
public class TableControlComponent implements TableViewer {
@XmlRootElement
public static class QueryTableConfig implements Serializable {
private boolean showTableControl = true;
private boolean useCopyOfTable = false;
public boolean isShowTableControl() {
return showTableControl;
}
@XmlAttribute
public void setShowTableControl(boolean showTableControl) {
this.showTableControl = showTableControl;
}
public boolean isUseCopyOfTable() {
return useCopyOfTable;
}
@XmlAttribute
public void setUseCopyOfTable(boolean useCopyOfTable) {
this.useCopyOfTable = useCopyOfTable;
}
}
// public static String ID = "com.opendoorlogistics.studio.uicomponents.showtable";
@Override
public String getName() {
return "Show table";
}
protected String panelName() {
return "TableGrid";
}
@Override
public String getId() {
return "com.opendoorlogistics.studio.uicomponents.showtable";
}
@Override
public long getFlags(ODLApi api, int mode) {
return ODLComponent.FLAG_OUTPUT_WINDOWS_CAN_BE_SYNCHRONISED | ODLComponent.FLAG_ALLOW_USER_INTERACTION_WHEN_RUNNING;
}
@Override
public ODLDatastore<? extends ODLTableDefinition> getIODsDefinition(ODLApi api, Serializable configuration) {
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> ret = ODLDatastoreImpl.alterableFactory.create();
ODLTableDefinitionAlterable table = ret.createTable("Table", -1);
table.setFlags(table.getFlags() | TableFlags.FLAG_COLUMN_WILDCARD | TableFlags.FLAG_TABLE_NAME_WILDCARD | TableFlags.FLAG_TABLE_NAME_USE_SOURCE);
return ret;
}
@Override
public ODLDatastore<? extends ODLTableDefinition> getOutputDsDefinition(ODLApi api, int mode, Serializable configuration) {
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> ret = api.tables().createDefinitionDs();
ret.setFlags(ret.getFlags() | TableFlags.FLAG_TABLE_WILDCARD);
return ret;
}
@Override
public void execute(final ComponentExecutionApi reporter, int mode, final Object configuration, ODLDatastore<? extends ODLTable> ioDb,
final ODLDatastoreAlterable<? extends ODLTableAlterable> outputDb) {
QueryTableConfig qtc = (QueryTableConfig) configuration;
if (qtc == null || qtc.isShowTableControl()) {
// Check if we should work with a copy of the table instead; we won't be able to use the table control to
// write back to the original table, but if the table was generated by a slow query, this will stop issues
// with functions in the query being re-run whenever a value is read, and hence slowing everything down
if (qtc.isUseCopyOfTable() && ioDb.getTableCount() > 0) {
reporter.postStatusMessage("Creating copy of table " + ioDb.getTableAt(0).getName() + " for table control, as data editing is disabled.");
// replace input datastore with a copy containing only the table
Tables tables = reporter.getApi().tables();
ODLDatastoreAlterable<? extends ODLTableAlterable> alterableDs = tables.createAlterableDs();
ODLTableAlterable copied=tables.copyTable(ioDb.getTableAt(0), alterableDs);
alterableDs.setFlags(alterableDs.getFlags() & ~TableFlags.UI_EDIT_PERMISSION_FLAGS);
copied.setFlags(copied.getFlags() & ~TableFlags.UI_EDIT_PERMISSION_FLAGS);
ioDb = alterableDs;
}
if (ioDb.getTableCount() > 0) {
reporter.submitControlLauncher(createControlLauncher(ioDb, ioDb.getTableAt(0).getImmutableId()));
}
}
}
private ControlLauncherCallback createControlLauncher(final ODLDatastore<? extends ODLTable> ioDb, int tableId) {
return new ControlLauncherCallback() {
@Override
public void launchControls(ComponentControlLauncherApi launcherApi) {
ODLTable table = ioDb.getTableByImmutableId(tableId);
if (table == null) {
return;
}
// check for row-colour column ?
final int colourColIndx = TableUtils.findColumnIndx(table, PredefinedTags.ROW_FONT_COLOUR);
if (colourColIndx != -1) {
// create an adapted datastore without the column
Tables tableApis = launcherApi.getApi().tables();
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> tmpDs = tableApis.createDefinitionDs();
ODLTableDefinitionAlterable tmpTable = tableApis.copyTableDefinition(table, tmpDs);
tmpTable.deleteColumn(colourColIndx);
AdapterMapping mapping = AdapterMapping.createUnassignedMapping(tmpDs);
mapping.setTableSourceId(tmpTable.getImmutableId(), 0, table.getImmutableId());
for (int col = 0; col < tmpTable.getColumnCount(); col++) {
int srcCol = col < colourColIndx ? col : col + 1;
mapping.setFieldSourceIndx(tmpTable.getImmutableId(), col, srcCol);
}
ArrayList<ODLDatastore<? extends ODLTable>> list = new ArrayList<>();
list.add(ioDb);
AdaptedDecorator<ODLTable> decorator = new AdaptedDecorator<>(mapping, list);
// create row style object
RowStyler styler = new RowStyler() {
@Override
public Color getRowFontColour(long rowId) {
Object value = table.getValueById(rowId, colourColIndx);
if (value != null) {
return Colours.toColour(value);
}
return null;
}
};
launchTableControl(decorator, decorator.getTableAt(0).getImmutableId(), launcherApi, styler);
} else {
launchTableControl(ioDb, table.getImmutableId(), launcherApi, null);
}
// }
}
};
}
/**
* Show the table control. This should be run in the EDT
*
* @param ioDb
* @param tableId
* @param launcherApi
* @param styler
*/
private void launchTableControl(final ODLDatastore<? extends ODLTable> ioDb, int tableId, ComponentControlLauncherApi launcherApi, RowStyler styler) {
ODLTableReadOnly table = ioDb.getTableByImmutableId(tableId);
if (table == null) {
return;
}
// get panel if already registered...
String panelName = panelName() + " - " + table.getName();
JPanel panel = launcherApi.getRegisteredPanel(panelName);
boolean addTable = true;
if (panel != null && TableViewerPanel.class.isInstance(panel)) {
TableViewerPanel viewer = (TableViewerPanel) panel;
viewer.replaceData(ioDb, table.getImmutableId(), styler);
addTable = false;
}
if (addTable) {
// don't bother using listeners as they don't work for adapted tables..
TableViewerPanel viewer = new TableViewerPanel(ioDb, table.getImmutableId(), false, styler, launcherApi.getGlobalDatastore(), getPermissions(table));
launcherApi.registerPanel(panelName, table.getName(), viewer, true);
}
}
protected GridEditPermissions getPermissions(ODLTableDefinition table) {
return GridEditPermissions.create(table, false);
}
// protected abstract ODLDatastoreUndoable<ODLTableAlterable> getGlobalDatastore();
// protected abstract HasInternalFrames getOwner();
@Override
public Class<? extends Serializable> getConfigClass() {
return QueryTableConfig.class;
}
@Override
public JPanel createConfigEditorPanel(ComponentConfigurationEditorAPI factory, int mode, final Serializable config, boolean isFixedIO) {
QueryTableConfig qtc = (QueryTableConfig) config;
final JCheckBox showTableCheckbox = new JCheckBox("Show table control?", qtc.isShowTableControl());
showTableCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
qtc.setShowTableControl(showTableCheckbox.isSelected());
}
});
showTableCheckbox.setAlignmentX(JCheckBox.LEFT_ALIGNMENT);
final JCheckBox enableDataEditingBox = new JCheckBox("Enable data editing?", !qtc.isUseCopyOfTable());
enableDataEditingBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
qtc.setUseCopyOfTable(!enableDataEditingBox.isSelected());
}
});
enableDataEditingBox.setAlignmentX(JCheckBox.LEFT_ALIGNMENT);
enableDataEditingBox.setToolTipText(
"<html>If the data adapter query allows it, keep a live data link from the table control to the original data.<br>This lets you edit the original data using the table control.</html>");
JPanel panel = new JPanel();
panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(showTableCheckbox);
panel.add(Box.createRigidArea(new Dimension(12, 1)));
panel.add(enableDataEditingBox);
return panel;
// new TextEntryPanel("Show table with name" ,((ShowTableConfig)config).getTable() ,
// "Enter the table name to show. If this is empty, the first table is shown.",
// new TextEntryPanel.TextChangedListener(){
//
// @Override
// public void textChange(String newText) {
// ((ShowTableConfig)config).setTable(newText);
// }
//
// });
}
@Override
public void registerScriptTemplates(ScriptTemplatesBuilder templatesApi) {
templatesApi.registerTemplate("Show table view", "Show table view", "Show view of the table which can include filtering, calculated fields etc.",
getIODsDefinition(templatesApi.getApi(), null), new BuildScriptCallback() {
@Override
public void buildScript(ScriptOption builder) {
// build adapter first
ScriptAdapter adapter = builder.addDataAdapter("QueryInput");
// add table to adapter
String outputName = "";
ScriptInputTables input = builder.getInputTables();
if (input.size() > 0 && input.getSourceTable(0) != null) {
outputName = "Query " + input.getSourceTable(0).getName();
adapter.addSourcedTableToAdapter(input.getSourceDatastoreId(0), input.getSourceTable(0), input.getSourceTable(0));
} else {
outputName = "Query table";
adapter.addEmptyTable(outputName);
}
// create show instruction
builder.addInstruction(adapter.getAdapterId(), getId(), ODLComponent.MODE_DEFAULT, new QueryTableConfig());
// create output
builder.addCopyTable(adapter.getAdapterId(), adapter.getTable(0).getTableDefinition().getName(), OutputType.DO_NOT_OUTPUT, outputName);
}
});
}
@Override
public Icon getIcon(ODLApi api, int mode) {
return Icons.loadFromStandardPath("show-table-component.png");
}
@Override
public boolean isModeSupported(ODLApi api, int mode) {
return mode == ODLComponent.MODE_DEFAULT;
}
}