/*******************************************************************************
* 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.scripts.componentwizard;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.components.ODLComponent;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.api.tables.TableFlags;
import com.opendoorlogistics.codefromweb.BoundsPopupMenuListener;
import com.opendoorlogistics.core.api.impl.ODLApiImpl;
import com.opendoorlogistics.core.api.impl.scripts.ScriptInputTablesImpl;
import com.opendoorlogistics.core.api.impl.scripts.ScriptTemplatesImpl;
import com.opendoorlogistics.core.components.ODLGlobalComponents;
import com.opendoorlogistics.core.components.ODLWizardTemplateConfig;
import com.opendoorlogistics.core.scripts.ScriptConstants;
import com.opendoorlogistics.core.scripts.TargetIODsInterpreter;
import com.opendoorlogistics.core.scripts.elements.Option;
import com.opendoorlogistics.core.scripts.elements.Script;
import com.opendoorlogistics.core.scripts.utils.TableId;
import com.opendoorlogistics.core.scripts.wizard.ScriptGenerator;
import com.opendoorlogistics.core.scripts.wizard.ScriptGenerator.ScriptGeneratorInput;
import com.opendoorlogistics.core.tables.utils.ExampleData;
import com.opendoorlogistics.core.tables.utils.TableUtils;
import com.opendoorlogistics.core.utils.Pair;
import com.opendoorlogistics.core.utils.iterators.IteratorUtils;
import com.opendoorlogistics.core.utils.strings.Strings;
import com.opendoorlogistics.core.utils.ui.VerticalLayoutPanel;
import com.opendoorlogistics.studio.InitialiseStudio;
import com.opendoorlogistics.studio.scripts.editor.adapters.QueryAvailableTables;
public class SetupComponentWizard extends JDialog {
final private VerticalLayoutPanel panel = new VerticalLayoutPanel();
final private ODLApi api;
final private QueryAvailableTables queryAvailableTables;
boolean userSelectedOk=false;
private boolean mergeWithInputOption=false;
private ComponentWizardData data = new ComponentWizardData();
/**
* @param api
* @param queryAvailableTables
* @param data
* @return
*/
public static ScriptGeneratorInput getScriptGeneratorInput(ODLApi api, QueryAvailableTables queryAvailableTables, ComponentWizardData data) {
// get the selected component from the wizard data
ODLComponent component = getComponent(data);
if(component==null){
throw new RuntimeException("Invalid component wizard data. Component cannot be found: " + data.getComponent());
}
// get the component's selected template from the wizard data
List<ODLWizardTemplateConfig> configs = getTemplates(api, data);
if(configs==null || configs.size()==0 || data.getTemplateIndex()>=configs.size() || data.getTemplateIndex()<0){
throw new RuntimeException("Invalid component wizard data. Incorrect template index.");
}
ODLWizardTemplateConfig config = configs.get(data.getTemplateIndex());
// Construct the input tables object from the wizard data and the component's expected input
ScriptInputTablesImpl inputTables = new ScriptInputTablesImpl();
ODLDatastore<? extends ODLTableDefinition> iods = config.getExpectedIods();
for(TableId tableId : data.getTableIds()){
ODLTableDefinition source =null;
if(tableId!=null && tableId.isNull()==false){
source =queryAvailableTables.getTableDefinition(tableId.getDsId(), tableId.getTableName());
if(source==null){
throw new RuntimeException("Invalid component wizard data. Table cannot be found: " + tableId);
}
}
ODLTableDefinition target=null;
if(iods!=null && inputTables.size() < iods.getTableCount()){
target = iods.getTableAt(inputTables.size());
}
inputTables.add(tableId.getDsId(), source, target);
}
ScriptGeneratorInput sgi = new ScriptGeneratorInput(component, config, inputTables);
return sgi;
}
// public Script generateScript(){
// return generateScript(api, queryAvailableTables, data);
// }
//
public SetupComponentWizard(Window parent,ODLApi api, QueryAvailableTables queryAvailableTables) {
super(parent,ModalityType.APPLICATION_MODAL);
this.api = api;
this.queryAvailableTables = queryAvailableTables;
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(new JScrollPane(panel));
//setContentPane();
setTitle("Setup component wizard");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setResizable(false);
build(true);
if(parent!=null){
setLocationRelativeTo(parent);
}
}
// private static Border createDefaultBorder() {
// return BorderFactory.createEmptyBorder(5, 5, 5, 5);
// }
void build(boolean firstBuild) {
panel.removeAll();
buildComponentList();
buildSelectTemplateConfig();
buildSelectTables();
// add ok & cancel buttons
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
Dimension buttonSize = new Dimension(80, 26);
JButton ok = new JButton(new AbstractAction("Ok") {
@Override
public void actionPerformed(ActionEvent e) {
userSelectedOk = true;
dispose();
}
});
setSizes(ok, buttonSize);
buttonPanel.add(ok);
JButton cancel = new JButton(new AbstractAction("Cancel"){
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
setSizes(cancel, buttonSize);
buttonPanel.add(cancel);
panel.add(buttonPanel);
//Rectangle bounds = getBounds();
pack();
if (!firstBuild) {
// setBounds(bounds);
}else{
//setMinimumSize(new Dimension(300, 300));
}
}
private static void setSizes(Component component, Dimension size){
component.setMinimumSize(size);
component.setMaximumSize(size);
component.setPreferredSize(size);
component.setSize(size);
}
private void buildSelectTables() {
// get component
ODLComponent component = getComponent(data);
if (component == null) {
return;
}
// get template, checking its set
List<ODLWizardTemplateConfig> templates = getTemplates(api,data);
if (templates == null || data.getTemplateIndex() < 0 || data.getTemplateIndex() >= templates.size()) {
return;
}
ODLWizardTemplateConfig template = templates.get(data.getTemplateIndex());
// get expected iods and nb of tables
final ODLDatastore<? extends ODLTableDefinition> iods = template.getExpectedIods();
TargetIODsInterpreter interpreter = new TargetIODsInterpreter(api);
Pair<Integer, Integer> nbTablesRange = interpreter.getNbTablesRange(iods);
// validate list
while (data.getTableIds().size() > nbTablesRange.getSecond()) {
data.getTableIds().remove(data.getTableIds().size() - 1);
}
while (data.getTableIds().size() < nbTablesRange.getFirst()) {
data.getTableIds().add(new TableId(null, null));
}
for(int i =0 ; i<data.getTableIds().size();i++){
if(data.getTableIds().get(i)==null){
data.getTableIds().set(i, new TableId(null, null));
}
}
// replace any null with same name matches if we have them
if(queryAvailableTables!=null){
for(int i =0 ; i<data.getTableIds().size();i++){
TableId current =data.getTableIds().get(i);
if(current.isNull() && iods!=null && i<iods.getTableCount()){
String targetName = iods.getTableAt(i).getName();
OUTER_LOOP:
for(String datastore:queryAvailableTables.queryAvailableDatastores()){
for(String tableName : queryAvailableTables.queryAvailableTables(datastore)){
if(Strings.equalsStd(tableName, targetName)){
current.setDsId(datastore);
current.setTableName(tableName);;
break OUTER_LOOP;
}
}
}
}
}
}
// return if there's nothing to set
if (nbTablesRange.getSecond() == 0) {
return;
}
// // helper class to get the
// class TableIdToString {
// String toString(TableId tableId) {
// if (tableId == null || tableId.isNull()) {
// return "<not set>";
// }
// return tableId.toString();
// }
// }
// final TableIdToString tableIdToString = new TableIdToString();
// setup combo box class
class MyComboBox extends JComboBox<TableId> {
final int index;
public MyComboBox(int index) {
super(getTableIds());
this.index = index;
setEditable(false);
setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component ret = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
TableId tableId = (TableId)value;
if (tableId == null || tableId.isNull()) {
setText( "<not set>");
}else{
setText(tableId.toString());
}
return ret;
}
});
addPopupMenuListener(new BoundsPopupMenuListener(true, false));
addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// refill popup menu dynamically
removeAllItems();
for (TableId tableId : getTableIds()) {
addItem(tableId);
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// TODO Auto-generated method stub
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// TODO Auto-generated method stub
}
});
}
}
// add combo box for each table
for (int i = 0; i < data.getTableIds().size(); i++) {
boolean isDefined = i < iods.getTableCount();
if (isDefined) {
ODLTableDefinition table = iods.getTableAt(i);
panel.add(new JLabel("<html>Input table for <em>" + table.getName() + "</em>" + (TableUtils.isTableOptional(table) ? " (optional)" : "") + ":</html>"));
} else {
panel.add(new JLabel("<html>Input table:</html>"));
}
panel.addHalfWhitespace();
final MyComboBox combo = new MyComboBox(i);
combo.setSelectedItem(data.getTableIds().get(i));
combo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
TableId selected = (TableId)combo.getSelectedItem();
data.getTableIds().set(combo.index, selected);
}
});
if(isDefined){
panel.add(combo);
}else{
// wildcard tables can be removed
JButton removeButton = new JButton(new AbstractAction("Remove") {
@Override
public void actionPerformed(ActionEvent e) {
data.getTableIds().remove(combo.index);
build(false);
}
});
panel.addLine(combo, removeButton);
}
panel.addWhitespace();
}
// add 'add a table' button
if(TableUtils.hasFlag(iods, TableFlags.FLAG_TABLE_WILDCARD)){
JButton button=new JButton(new AbstractAction("Add a new table") {
@Override
public void actionPerformed(ActionEvent e) {
data.getTableIds().add(new TableId(null, null));
build(false);
}
});
panel.addWrapped(button, BorderLayout.WEST);
panel.addWhitespace();
}
}
/**
*
*/
private void buildComponentList() {
class ComponentIcon {
final ODLComponent component;
final Icon icon;
public ComponentIcon(ODLComponent component) {
this.component = component;
this.icon = component.getIcon(api, ODLComponent.MODE_DEFAULT);
}
}
ArrayList<ComponentIcon> cis = new ArrayList<>();
for (ODLComponent component : ODLGlobalComponents.getProvider()) {
cis.add(new ComponentIcon(component));
}
final JComboBox<ComponentIcon> combo = new JComboBox<>(cis.toArray(new ComponentIcon[cis.size()]));
combo.setEditable(false);
combo.setRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component ret = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
ComponentIcon ci = (ComponentIcon) value;
setIcon(ci.icon);
setText(ci.component.getName());
return ret;
}
});
combo.addPopupMenuListener(new BoundsPopupMenuListener(true, false));
// select the current component...
boolean hasSelection = false;
for (int i = 0; i < combo.getModel().getSize(); i++) {
ComponentIcon ci = combo.getModel().getElementAt(i);
if (Strings.equalsStd(ci.component.getId(), data.getComponent())) {
hasSelection = true;
combo.setSelectedIndex(i);
}
}
// set first if nothing selected
if (hasSelection == false && combo.getModel().getSize() > 0) {
data.setComponent(combo.getModel().getElementAt(0).component.getId());
combo.setSelectedIndex(0);
}
// add a selection changed listener
combo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
ComponentIcon ci = (ComponentIcon) combo.getSelectedItem();
if (ci != null) {
data.setComponent( ci.component.getId());
} else {
data.setComponent(null);
}
// rebuild dialog
build(false);
}
});
panel.add(new JLabel("<html>Select component:</html>"));
panel.addHalfWhitespace();
panel.add(combo);
panel.addWhitespace();
}
private void buildSelectTemplateConfig() {
List<ODLWizardTemplateConfig> list = getTemplates(api,data);
if (list == null) {
return;
}
// check if no choice is needed
if (list.size() < 2) {
data.setTemplateIndex(0);
return;
}
// validate the index
if (data.getTemplateIndex()<0 || data.getTemplateIndex() >= list.size()) {
data.setTemplateIndex(0);
}
class TemplateItem {
String s;
int index;
}
TemplateItem[] items = new TemplateItem[list.size()];
for (int i = 0; i < list.size(); i++) {
items[i] = new TemplateItem();
items[i].s = list.get(i).getName();
items[i].index = i;
}
// create combo
final JComboBox<TemplateItem> combo = new JComboBox<>(items);
combo.addPopupMenuListener(new BoundsPopupMenuListener(true, false));
// set renderer
combo.setRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component ret = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
TemplateItem item = (TemplateItem) value;
setText(item.s);
return ret;
}
});
// set selected
for (int i = 0; i < combo.getModel().getSize(); i++) {
TemplateItem item = combo.getModel().getElementAt(i);
if (item.index == data.getTemplateIndex()) {
combo.setSelectedIndex(i);
}
}
// add a selection changed listener
combo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
TemplateItem ci = (TemplateItem) combo.getSelectedItem();
if (ci != null) {
data.setTemplateIndex( ci.index);
} else {
data.setTemplateIndex( -1);
}
// rebuild dialog
build(false);
}
});
panel.add(new JLabel("<html>Select component configuration:</html>"));
panel.addHalfWhitespace();
panel.add(combo);
panel.addWhitespace();
}
/**
* @return
*/
private static List<ODLWizardTemplateConfig> getTemplates(ODLApi api,ComponentWizardData data) {
ODLComponent component = getComponent(data);
if (component == null) {
return null;
}
Iterable<ODLWizardTemplateConfig> templates = ScriptTemplatesImpl.getTemplates(api, component);
if (templates == null) {
return null;
}
List<ODLWizardTemplateConfig> list = IteratorUtils.toList(templates);
return list;
}
/**
* @return
*/
private static ODLComponent getComponent(ComponentWizardData data) {
if (Strings.isEmpty(data.getComponent())) {
return null;
}
ODLComponent component = ODLGlobalComponents.getProvider().getComponent(data.getComponent());
return component;
}
public static void main(String[] args) {
InitialiseStudio.initialise(false);
final ODLDatastore<? extends ODLTableDefinition> ds = ExampleData.createTerritoriesExample(4);
QueryAvailableTables queryAvailableTables = new QueryAvailableTables() {
@Override
public String[] queryAvailableTables(String datastore) {
List<String>ret = TableUtils.getTableNames(ds);
return ret.toArray(new String[ret.size()]);
}
@Override
public String[] queryAvailableDatastores() {
return new String[]{ScriptConstants.EXTERNAL_DS_NAME};
}
@Override
public ODLTableDefinition getTableDefinition(String datastore, String tablename) {
return TableUtils.findTable(ds, tablename);
}
@Override
public ODLDatastore<? extends ODLTableDefinition> getDatastoreDefinition(String datastore) {
if(Strings.equalsStd(ScriptConstants.EXTERNAL_DS_NAME, datastore)){
return ds;
}
return null;
}
};
new SetupComponentWizard(null,new ODLApiImpl(), queryAvailableTables).showModal();
//ODLInternalFrame.showInDummyDesktopPane(new ComponentWizard(new ODLApiImpl(), queryAvailableTables));
}
// public static void main(String[] args) {
// String title = "Desktop Sample";
// JFrame frame = new JFrame(title);
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//
// JDesktopPane desktop = new JDesktopPane();
// JInternalFrame internalFrame = new JInternalFrame("Can Do All", true, true, true, true);
//
// desktop.add(internalFrame);
// internalFrame.setBounds( 25, 25, 200, 100);
//
// internalFrame.setVisible(true);
//
// Container content = frame.getContentPane();
// content.add(desktop, BorderLayout.CENTER);
// frame.setSize(500, 300);
// frame.setVisible(true);
// }
private TableId[] getTableIds() {
ArrayList<TableId> ret = new ArrayList<>();
// add 'not set'
ret.add(new TableId(null, null));
// add other tables
if(queryAvailableTables!=null){
for (String datastore : queryAvailableTables.queryAvailableDatastores()){
for(String table:queryAvailableTables.queryAvailableTables(datastore)){
ret.add(new TableId(datastore, table));
}
}}
return ret.toArray(new TableId[ret.size()]);
}
public ComponentWizardData getData() {
return data;
}
public void setData(ComponentWizardData data) {
this.data = data;
build(false);
}
public Script showModal(){
return (Script)showModal(null, null);
}
public Option showModal(Script script, Option parent){
// rebuild in case data has changed
build(false);
setVisible(true);
if(userSelectedOk){
ScriptGeneratorInput sgi = getScriptGeneratorInput(api, queryAvailableTables, data);
Option option = ScriptGenerator.generate(api, script, parent, sgi);
if(mergeWithInputOption && parent!=null){
parent.getOptions().remove(option);
parent.mergeIntoMe(option);
}
return option;
}
return null;
}
public boolean isMergeWithInputOption() {
return mergeWithInputOption;
}
/**
* Merge the new option with the input parent option
* @param mergeWithInputOption
*/
public void setMergeWithInputOption(boolean mergeWithInputOption) {
this.mergeWithInputOption = mergeWithInputOption;
}
}