/*
* This file is part of Alida, a Java library for
* Advanced Library for Integrated Development of Data Analysis Applications.
*
* Copyright (C) 2010 - @YEAR@
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Fore more information on Alida, visit
*
* http://www.informatik.uni-halle.de/alida/
*
*/
package de.unihalle.informatik.Alida.dataio.provider.swing;
import de.unihalle.informatik.Alida.annotations.ALDDataIOProvider;
import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerSwing;
import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerSwing.*;
import de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwingInitialGUIValueDefaultHandler;
import de.unihalle.informatik.Alida.dataio.provider.swing.components.*;
import de.unihalle.informatik.Alida.dataio.provider.swing.events.*;
import de.unihalle.informatik.Alida.exceptions.*;
import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException.*;
import de.unihalle.informatik.Alida.operator.ALDParameterDescriptor;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.*;
/**
* Class for generic loading/saving of 1D-arrays from/to GUI in Alida.
*
* @author moeller
*/
@ALDDataIOProvider
public class ALDNativeArray1DDataIOSwing
extends ALDDataIOSwingInitialGUIValueDefaultHandler {
/**
* List of supported classes.
*/
private static LinkedList<Class<?>> classes = null;
/**
* Default constructor.
*/
public ALDNativeArray1DDataIOSwing() {
if (classes == null) {
classes = new LinkedList<Class<?>>();
ALDNativeArray1DDataIOSwing.classes.add( Boolean[].class);
ALDNativeArray1DDataIOSwing.classes.add( Byte[].class);
ALDNativeArray1DDataIOSwing.classes.add( Double[].class);
ALDNativeArray1DDataIOSwing.classes.add( Float[].class);
ALDNativeArray1DDataIOSwing.classes.add( Integer[].class);
ALDNativeArray1DDataIOSwing.classes.add( Short[].class);
ALDNativeArray1DDataIOSwing.classes.add( String[].class);
ALDNativeArray1DDataIOSwing.classes.add( boolean[].class);
ALDNativeArray1DDataIOSwing.classes.add( byte[].class);
ALDNativeArray1DDataIOSwing.classes.add( double[].class);
ALDNativeArray1DDataIOSwing.classes.add( float[].class);
ALDNativeArray1DDataIOSwing.classes.add( int[].class);
ALDNativeArray1DDataIOSwing.classes.add( short[].class);
}
}
/**
* Interface method to announce class for which IO is provided for.
*
* @return Collection of classes provided.
*/
@SuppressWarnings("unchecked")
@Override
public Collection<Class<?>> providedClasses() {
return (Collection<Class<?>>)classes.clone();
}
/**
* Generic reading of 1D arrays.
*/
/* (non-Javadoc)
* @see de.unihalle.informatik.Alida.helpers.ALDDataIOSwing#createGUIElement(java.lang.Class, java.lang.Object)
*/
@Override
public ALDSwingComponent createGUIElement(Field field, Class<?> cl,
Object obj, ALDParameterDescriptor descr) {
return new ArrayConfigPanel(field, cl, obj, descr);
}
/* (non-Javadoc)
* @see de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwing#setValue(java.lang.reflect.Field, java.lang.Class, de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDSwingComponent, java.lang.Object)
*/
@Override
public void setValue(Field field, Class<?> cl,
ALDSwingComponent guiElement, Object value)
throws ALDDataIOProviderException {
if (value == null)
return;
if (!(guiElement instanceof ArrayConfigPanel))
throw new ALDDataIOProviderException(
ALDDataIOProviderExceptionType.INVALID_GUI_ELEMENT,
"[NativeArray1DDataIO] setValue() received invalid GUI element!");
if (!(classes.contains(value.getClass())))
throw new ALDDataIOProviderException(
ALDDataIOProviderExceptionType.INVALID_GUI_ELEMENT,
"[NativeArray1DDataIO] setValue() received wrong object type!");
((ArrayConfigPanel)guiElement).setValue(field, cl, value);
}
/* (non-Javadoc)
* @see de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwing#readData(java.lang.reflect.Field, java.lang.Class, de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDSwingComponent)
*/
@Override
public Object readData(
Field field, Class<?> cl, ALDSwingComponent guiElement)
throws ALDDataIOProviderException {
if (!(guiElement instanceof ArrayConfigPanel))
throw new ALDDataIOProviderException(
ALDDataIOProviderExceptionType.INVALID_GUI_ELEMENT,
"[NativeArray1DDataIO] readData() received invalid GUI element!");
return ((ArrayConfigPanel)guiElement).readData(field, cl);
}
/* (non-Javadoc)
* @see de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwing#writeData(java.lang.Object)
*/
@Override
public JComponent writeData(Object obj, ALDParameterDescriptor descr)
throws ALDDataIOProviderException {
if (!(classes.contains(obj.getClass())))
throw new ALDDataIOProviderException(
ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR,
"[NativeArray1DDataIO] object to write has wrong type!");
// return a button to show a window with the elements
return new ArrayShowButton(obj, descr);
}
/**
* GUI element for configuring 1D arrays of native data types and
* wrapper.
* <p>
* This button has an array configuration window attached to it
* where specific data is stored and accessable.
*
* @author moeller
*/
private class ArrayConfigPanel extends ALDSwingComponent
implements ALDSwingValueChangeListener, ActionListener {
/**
* GUI component associated with this Swing component.
*/
private JPanel configPanel;
/**
* Button to create and configure array.
*/
private JButton confButton;
/**
* Button to reset array.
*/
private JButton resetButton;
/**
* Array configuration window.
*/
private ArrayConfigWindow confWin;
/**
* Class of array to configure.
*/
private Class<?> arrayClass;
/**
* Parameter field of array to configure.
*/
private Field arrayField;
/**
* Descriptor of parameter linked to array.
*/
private ALDParameterDescriptor arrayDescriptor;
/**
* Constructor.
*
* @param field Field of collection.
* @param cl Class of collection.
* @param obj Default object.
* @param descr Optional descriptor for additional information.
*/
public ArrayConfigPanel(Field field, Class<?> cl, Object obj,
ALDParameterDescriptor descr) {
this.arrayClass = cl;
this.arrayField = field;
this.arrayDescriptor = descr;
this.confButton = new JButton("Configure Native Array...");
this.confButton.setActionCommand("show");
this.confButton.addActionListener(this);
this.resetButton = new JButton("Reset");
this.resetButton.setActionCommand("reset");
this.resetButton.addActionListener(this);
this.configPanel = new JPanel();
this.configPanel.add(this.confButton);
this.configPanel.add(this.resetButton);
if (obj != null) {
this.confWin = new ArrayConfigWindow(field, cl, obj, descr);
this.confWin.addValueChangeEventListener(this);
}
}
@Override
public JComponent getJComponent() {
return this.configPanel;
}
/**
* Gets the data from the configuration window.
*
* @param field Field of collection.
* @param cl Class of collection.
* @param value Default object.
*/
public void setValue(Field field, Class<?> cl, Object value) {
if (this.confWin != null)
this.confWin.setValue(field, cl, value);
else {
this.confWin =
new ArrayConfigWindow(field, cl, value, this.arrayDescriptor);
this.confWin.addValueChangeEventListener(this);
}
}
/**
* Gets the data from the configuration window.
*
* @param field Field of collection.
* @param cl Class of collection.
* @return Current data.
* @throws ALDDataIOProviderException Thrown in case of read failures.
*/
public Object readData(Field field, Class<?> cl)
throws ALDDataIOProviderException {
if (this.confWin == null)
return null;
return this.confWin.readData(field, cl);
}
/* (non-Javadoc)
* @see de.unihalle.informatik.Alida.dataio.provider.swing.events.ALDSwingValueChangeListener#handleValueChangeEvent(de.unihalle.informatik.Alida.dataio.provider.swing.events.ALDSwingValueChangeEvent)
*/
@Override
public void handleValueChangeEvent(ALDSwingValueChangeEvent event) {
this.fireALDSwingValueChangeEvent(event);
}
@Override
public void disableComponent() {
if (this.confWin != null)
this.confWin.disableComponent();
}
@Override
public void enableComponent() {
if (this.confWin != null)
this.confWin.enableComponent();
}
@Override
public void dispose() {
if (this.confWin != null)
this.confWin.dispose();
}
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("reset")) {
if (this.confWin != null)
this.confWin.dispose();
this.confWin = null;
this.handleValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.arrayDescriptor));
}
else if (cmd.equals("show")) {
if (this.confWin != null)
this.confWin.setVisible(true);
else {
this.confWin = new ArrayConfigWindow(this.arrayField,
this.arrayClass, null, this.arrayDescriptor);
this.confWin.addValueChangeEventListener(this);
this.confWin.setVisible(true);
this.handleValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.arrayDescriptor));
}
}
}
}
/**
* GUI element for displaying 2D arrays.
*
* @author moeller
*/
private class ArrayShowButton extends JButton
implements ActionListener{
/**
* Data to be displayed.
*/
private Object data;
/**
* Optional descriptor for additional information on parameter.
*/
private ALDParameterDescriptor descriptor;
/**
* Constructor.
* @param obj Object to show in GUI.
* @param descr Descriptor associated with operator parameter.
*/
public ArrayShowButton(Object obj, ALDParameterDescriptor descr) {
super("Show data...");
this.setActionCommand("showButtonPressed");
this.addActionListener(this);
this.data = obj;
this.descriptor = descr;
}
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("showButtonPressed")) {
Class<?> cl = this.data.getClass();
DefaultTableModel outTabModel = null;
if (cl.equals(Boolean[].class)) {
Boolean [] field = (Boolean[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(Byte[].class)) {
Byte [] field = (Byte[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(Double[].class)) {
Double [] field = (Double[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(Float[].class)) {
Float [] field = (Float[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(Integer[].class)) {
Integer [] field = (Integer[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(Short[].class)) {
Short [] field = (Short[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(String[].class)) {
String [] field = (String[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(field[h], h, 0);
}
}
else if (cl.equals(boolean[].class)) {
boolean [] field = (boolean[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(new Boolean(field[h]), h, 0);
}
}
else if (cl.equals(byte[].class)) {
byte [] field = (byte[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(new Byte(field[h]), h, 0);
}
}
else if (cl.equals(double[].class)) {
double [] field = (double[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(new Double(field[h]), h, 0);
}
}
else if (cl.equals(float[].class)) {
float [] field = (float[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(new Float(field[h]), h, 0);
}
}
else if (cl.equals(int[].class)) {
int [] field = (int[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(new Integer(field[h]), h, 0);
}
}
else if (cl.equals(short[].class)) {
short [] field = (short[])this.data;
int height = field.length;
outTabModel = new DefaultTableModel(height, 1) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}};
for (int h=0;h<height;++h) {
outTabModel.setValueAt(new Short(field[h]), h, 0);
}
}
ALDTableWindow resultTable = new ALDTableWindow(outTabModel,
this.data);
String paramName = "<unknown>";
if (this.descriptor != null)
paramName = "<" + this.descriptor.getLabel() + ">";
resultTable.setTitle("Result data (" + cl.getSimpleName()
+ ") for parameter " + paramName);
resultTable.openWindow();
}
}
}
/**
* Graphical user interface window for displaying tables.
*
* @author moeller
*/
private class ArrayConfigWindow extends ALDSwingValueChangeReporter
implements ActionListener, TableModelListener {
/**
* Main frame.
*/
private JFrame window = null;
/**
* Data table (swing tables are nicer than imageJ tables).
*/
protected JTable dataTab;
/**
* Reference object to the data.
*/
private DefaultTableModel dataTabModel;
/**
* Table component in window.
*/
private JPanel tabPanel;
/**
* Scrollpane for table panel;
*/
private JScrollPane tableScroller;
/**
* Button for adding a row.
*/
private JButton addRow;
/**
* Button for deleting a row.
*/
private JButton delRow;
/**
* Button to save the table.
*/
private JButton tabSave;
/**
* Button to load a table.
*/
private JButton tabLoad;
/**
* Button to clear the table.
*/
private JButton tabClear;
/**
* Button to reset the table.
*/
private JButton tabReset;
/**
* For convenience: always open last directory for saving.
*/
private File lastDir;
/**
* Class that table entries should represent.
*/
private Class<?> elementClass;
/**
* Optional descriptor for additional information on parameter.
*/
protected ALDParameterDescriptor descriptor;
/**
* List of currently specified values (to restore entry if check
* fails).
*/
private Object[] entryRefList;
/**
* Flag to disable entry validation checks.
* <p>
* Note that this flag is only valid for next event appearing. While
* processing the event the flag is reset to false.
*/
private boolean disableValueChecks = false;
/**
* Flag to indicate the table window has the focus.
* <p>
* By this multiple warnings on the same issue should be avoided.
* They might happen due to table change events and focus change
* events being triggered at the same time.
*/
protected boolean windowHasFocus = false;
/**
* Default constructor.
* @param field Field of array.
* @param cl Class of array elements.
* @param obj Default values.
* @param descr Descriptor associated with operator parameter.
*/
public ArrayConfigWindow(Field field, Class<?> cl, Object obj,
ALDParameterDescriptor descr) {
// instantiate the main window
this.window = new JFrame();
// init dummy table, needs at least one cell; overwritten if obj
// non-null
this.dataTabModel = new DefaultTableModel(1, 1);
this.dataTabModel.addTableModelListener(this);
this.entryRefList = new Object[1];
// fill with default values
this.setTabEntryToDefault(0, cl);
// remember the class of the array
this.elementClass = cl;
// store additional descriptor
this.descriptor = descr;
// init current directory with user directory
this.lastDir= new File(System.getProperty("user.dir"));
// build table window
this.setupTable();
// if available, fill table with default values
if (obj != null)
this.setValue(field, cl, obj);
}
/**
* Show or hide the configuration window.
* @param b If true, window is displayed, otherwise it is hidden.
*/
public void setVisible(boolean b) {
this.window.setVisible(b);
}
/**
* Disable table and buttons to prohibit value changes.
*/
public void disableComponent() {
if (this.dataTab != null)
this.dataTab.setEnabled(false);
if (this.addRow != null)
this.addRow.setEnabled(false);
if (this.delRow != null)
this.delRow.setEnabled(false);
if (this.tabClear != null)
this.tabClear.setEnabled(false);
if (this.tabReset != null)
this.tabReset.setEnabled(false);
}
/**
* Enable table and buttons to allow for value changes.
*/
public void enableComponent() {
if (this.dataTab != null)
this.dataTab.setEnabled(true);
if (this.addRow != null)
this.addRow.setEnabled(true);
if (this.delRow != null)
this.delRow.setEnabled(true);
if (this.tabClear != null)
this.tabClear.setEnabled(true);
if (this.tabReset != null)
this.tabReset.setEnabled(true);
}
/**
* Disposes all resources of this window.
*/
public void dispose() {
this.window.dispose();
}
/**
* Sets the specified table entry to the class default.
* @param row Row to set.
* @param cl Class of table elements.
*/
protected void setTabEntryToDefault(int row, Class<?> cl) {
if (cl.equals(boolean[].class)) {
boolean tmpBool = false;
this.dataTabModel.setValueAt(Boolean.toString(tmpBool), row, 0);
}
else if (cl.equals(byte[].class)) {
byte tmpByte = 0;
this.dataTabModel.setValueAt(Byte.toString(tmpByte), row, 0);
}
else if (cl.equals(double[].class)) {
double tmpDouble = 0;
this.dataTabModel.setValueAt(Double.toString(tmpDouble), row, 0);
}
else if (cl.equals(float[].class)) {
float tmpFloat = 0;
this.dataTabModel.setValueAt(Float.toString(tmpFloat), row, 0);
}
else if (cl.equals(int[].class)) {
int tmpInt = 0;
this.dataTabModel.setValueAt(Integer.toString(tmpInt), row, 0);
}
else if (cl.equals(short[].class)) {
short tmpShort = 0;
this.dataTabModel.setValueAt(Short.toString(tmpShort), row, 0);
}
else if (cl.equals(Boolean[].class)) {
Boolean tmpBool = new Boolean(false);
this.dataTabModel.setValueAt(tmpBool.toString(), row, 0);
}
else if (cl.equals(Byte[].class)) {
Byte tmpByte = new Byte((byte)0);
this.dataTabModel.setValueAt(tmpByte.toString(), row, 0);
}
else if (cl.equals(Double[].class)) {
Double tmpDouble = new Double(0);
this.dataTabModel.setValueAt(tmpDouble.toString(), row, 0);
}
else if (cl.equals(Float[].class)) {
Float tmpFloat = new Float(0);
this.dataTabModel.setValueAt(tmpFloat.toString(), row, 0);
}
else if (cl.equals(Integer[].class)) {
Integer tmpInt = new Integer(0);
this.dataTabModel.setValueAt(tmpInt.toString(), row, 0);
}
else if (cl.equals(Short[].class)) {
Short tmpShort = new Short((short)0);
this.dataTabModel.setValueAt(tmpShort.toString(), row, 0);
}
else if (cl.equals(String[].class)) {
this.dataTabModel.setValueAt(new String(), row, 0);
}
// store the value for later value checking
this.entryRefList[row] = this.dataTabModel.getValueAt(row,0);
}
/**
* Fills the table with specified values.
* @param field Field specifying type of data.
* @param cl Class of array entries.
* @param value Value to set.
*/
public void setValue(@SuppressWarnings("unused") Field field,
Class<?> cl, Object value) {
// there is nothing to do if value is null
if (value == null)
return;
if ( cl.equals(Boolean[].class)
|| cl.equals(Byte[].class)
|| cl.equals(Double[].class)
|| cl.equals(Float[].class)
|| cl.equals(Integer[].class)
|| cl.equals(Short[].class)
|| cl.equals(String[].class)) {
Object [] array = (Object[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
Object val = array[r];
String entry = new String();
if (val != null)
entry = val.toString();
this.dataTabModel.setValueAt(entry, r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
else if (cl.equals(boolean[].class)) {
boolean [] array = (boolean[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
this.dataTabModel.setValueAt(Boolean.toString(array[r]), r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
else if (cl.equals(byte[].class)) {
byte [] array = (byte[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
this.dataTabModel.setValueAt(Byte.toString(array[r]), r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
else if (cl.equals(double[].class)) {
double [] array = (double[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
this.dataTabModel.setValueAt(Double.toString(array[r]), r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
else if (cl.equals(float[].class)) {
float [] array = (float[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
this.dataTabModel.setValueAt(Float.toString(array[r]), r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
else if (cl.equals(int[].class)) {
int [] array = (int[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
this.dataTabModel.setValueAt(Integer.toString(array[r]), r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
else if (cl.equals(short[].class)) {
short [] array = (short[])value;
int rows = array.length;
this.dataTabModel = new DefaultTableModel(rows,1);
this.entryRefList = new Object[rows];
for (int r=0;r<rows;++r) {
this.dataTabModel.setValueAt(Short.toString(array[r]), r, 0);
this.entryRefList[r] = this.dataTabModel.getValueAt(r,0);
}
}
// update the view
this.dataTabModel.addTableModelListener(this);
this.dataTab.setModel(this.dataTabModel);
this.tabPanel.updateUI();
}
/**
* Read data from graphical component.
* @param field Field of underlying parameter.
* @param cl Class of underlying parameter.
* @return Value of the parameter in GUI.
* @throws ALDDataIOProviderException Thrown on read failures.
*/
@SuppressWarnings({ "unchecked" })
public Object readData(@SuppressWarnings("unused") Field field,
Class<?> cl)
throws ALDDataIOProviderException {
// check if there is already data to check
if (this.dataTabModel == null) {
System.out.println("Data tab model is null!");
return null;
}
// list for collecting errors
LinkedList<Integer> errors = new LinkedList<Integer>();
// get data, each vector entry is again of type Vector<Object>
Vector<Object> data = this.dataTabModel.getDataVector();
// check if table contains at least one non-null element
int lastNonNullIndex = data.size()-1;
if (!cl.equals(String[].class)) {
lastNonNullIndex = data.size();
for (int i= data.size()-1; i>= 0; --i) {
if ( data.elementAt(i) == null
|| ((Vector<Object>)data.elementAt(i)).isEmpty()) {
continue;
}
if (((Vector<Object>)data.elementAt(i)).elementAt(0) != null) {
lastNonNullIndex = i;
break;
}
}
// ... if not, return null
if (lastNonNullIndex == data.size()) {
return null;
}
}
int rows = lastNonNullIndex+1;
Object entry;
if (cl.equals(Boolean[].class)) {
Boolean [] tmpfield = new Boolean[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
}
else {
try {
tmpfield[r] = Boolean.valueOf((String)(entry));
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(Byte[].class)) {
Byte [] tmpfield = new Byte[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Byte.valueOf((String)(entry));
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(Double[].class)) {
Double [] tmpfield = new Double[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Double.valueOf((String)(entry));
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(Float[].class)) {
Float [] tmpfield = new Float[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Float.valueOf((String)(entry));
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(Integer[].class)) {
Integer [] tmpfield = new Integer[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Integer.valueOf((String)(entry));
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(Short[].class)) {
Short [] tmpfield = new Short[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Short.valueOf((String)(entry));
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(String[].class)) {
String [] tmpfield = new String[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
tmpfield[r] = new String();
// tmpfield[r] = null;
}
else {
tmpfield[r] = (String)entry;
}
}
// if (errors.size() > 0)
// this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(boolean[].class)) {
boolean [] tmpfield = new boolean[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Boolean.valueOf((String)(entry)).booleanValue();
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(byte[].class)) {
byte [] tmpfield = new byte[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Byte.valueOf((String)(entry)).byteValue();
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(double[].class)) {
double [] tmpfield = new double[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Double.valueOf((String)(entry)).doubleValue();
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(float[].class)) {
float [] tmpfield = new float[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Float.valueOf((String)(entry)).floatValue();
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(int[].class)) {
int [] tmpfield = new int[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Integer.valueOf((String)(entry)).intValue();
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
if (cl.equals(short[].class)) {
short [] tmpfield = new short[rows];
for (int r=0;r<rows;++r) {
entry = ((Vector<Object>)data.elementAt(r)).elementAt(0);
if (entry == null) {
errors.add(new Integer(r));
continue;
}
try {
tmpfield[r] = Short.valueOf((String)(entry)).shortValue();
} catch (NumberFormatException ne) {
errors.add(new Integer(r));
}
}
if (errors.size() > 0)
this.errorListToMessage(errors);
return tmpfield;
}
return null;
}
/**
* Method to resize the reference entry list.
* <p>
* Existing entries are preserved as far as possible.
* @param newSize New size for the table.
*/
protected void resizeTableEntries(int newSize) {
Object[] newEntries = new Object[newSize];
int commonSize = (newSize > this.entryRefList.length ?
this.entryRefList.length : newSize);
for (int i=0;i<commonSize;++i) {
newEntries[i] = this.entryRefList[i];
}
this.entryRefList = newEntries;
}
/**
* Checks if a string can be cast to the desired class.
* @param cl Target class.
* @param entry String under consideration.
* @return True if cast is possible.
*/
@SuppressWarnings({ "unused" })
protected boolean validateEntry(Class<?> cl, String entry) {
if (entry == null)
return false;
// a string is allowed to be empty
if (entry.isEmpty() && !cl.equals(String[].class))
return false;
if (cl.equals(Boolean[].class)) {
try {
Boolean t = Boolean.valueOf((entry));
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(Byte[].class)) {
try {
Byte t = Byte.valueOf((entry));
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(Double[].class)) {
try {
Double t = Double.valueOf((entry));
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(Float[].class)) {
try {
Float t = Float.valueOf((entry));
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(Integer[].class)) {
try {
Integer t = Integer.valueOf((entry));
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(Short[].class)) {
try {
Short t = Short.valueOf((entry));
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(String[].class)) {
return true;
}
else if (cl.equals(boolean[].class)) {
try {
boolean t = Boolean.valueOf((entry)).booleanValue();
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(byte[].class)) {
try {
byte t = Byte.valueOf((entry)).byteValue();
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(double[].class)) {
try {
double t = Double.valueOf((entry)).doubleValue();
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(float[].class)) {
try {
float t = Float.valueOf((entry)).floatValue();
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(int[].class)) {
try {
int t = Integer.valueOf((entry)).intValue();
} catch (NumberFormatException ne) {
return false;
}
}
else if (cl.equals(short[].class)) {
try {
short t = Short.valueOf((entry)).shortValue();
} catch (NumberFormatException ne) {
return false;
}
}
return true;
}
/**
* Saves the contents of the table to a user-specified file.
* <p>
* The file format is TSV, i.e. tabulator-separated values.
* The default ending is '.txt'. The user can select the file
* name through a file open dialog which pops-up on call of the
* function.
*/
protected void saveTable() {
File file= null;
JFrame dummy= new JFrame();
JFileChooser getSaveFileDialog= new JFileChooser();
getSaveFileDialog.setFileFilter(new DataTabFileFilter());
getSaveFileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
getSaveFileDialog.setCurrentDirectory(this.lastDir);
getSaveFileDialog.setSelectedFile(new File("ResultTab.txt"));
getSaveFileDialog.setApproveButtonText("Speichern");
getSaveFileDialog.setDialogTitle("Select file name");
int returnVal = getSaveFileDialog.showOpenDialog(dummy);
if (returnVal == JFileChooser.APPROVE_OPTION) {
file = getSaveFileDialog.getSelectedFile();
this.lastDir= getSaveFileDialog.getCurrentDirectory();
}
// obviously we did not get any name, can't do anything...
if (file== null)
return;
// ... otherwise write table to selected file
try{
if (file.exists()) {
int ret=
JOptionPane.showConfirmDialog(null,
"The file exists already, would "
+ "you like to overwrite it?", "Warning",
JOptionPane.YES_NO_OPTION);
switch(ret)
{
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
return;
case JOptionPane.YES_OPTION:
// nothing special to do, just proceed
}
}
FileWriter ow= new FileWriter(file.getPath());
StringBuffer [] tab= ALDTableWindow.tableToString(
this.dataTabModel);
// note: first row contains the header which is meaningless in
// this context, therefore it is ignored
for (int i=1;i<tab.length;++i)
ow.write(tab[i].toString());
ow.close();
} catch (IOException e) {
JOptionPane.showMessageDialog(null,"Error!!! " +
"Could not open output file " + file.getPath() + "!");
}
}
/**
* Loads contents of the table from a user-specified file.
* <p>
* The file format is TSV, i.e. tabulator-separated values.
* The default ending is '.txt'. The user can select the file
* name through a file open dialog which pops-up on call of the
* function.
*/
protected void loadTable() {
// request the file from the user
File file= null;
JFrame dummy= new JFrame();
JFileChooser getSaveFileDialog= new JFileChooser();
getSaveFileDialog.setFileFilter(new DataTabFileFilter());
getSaveFileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
getSaveFileDialog.setCurrentDirectory(this.lastDir);
getSaveFileDialog.setSelectedFile(new File("ResultTab.txt"));
getSaveFileDialog.setApproveButtonText("Open");
getSaveFileDialog.setDialogTitle("Select file name");
int returnVal = getSaveFileDialog.showOpenDialog(dummy);
if (returnVal == JFileChooser.APPROVE_OPTION) {
file = getSaveFileDialog.getSelectedFile();
this.lastDir= getSaveFileDialog.getCurrentDirectory();
}
// obviously we did not get any name, can't do anything...
if (file== null)
return;
// ... otherwise load table from selected file
try{
if (!file.exists()) {
JOptionPane.showMessageDialog(null, "The file does not exist!",
"Error reading file!", JOptionPane.ERROR_MESSAGE);
return;
}
FileInputStream fis = new FileInputStream(file.getPath());
BufferedReader br =
new BufferedReader(new InputStreamReader(fis));
Vector<String> entries = new Vector<String>();
String line = br.readLine();
while( line != null ) {
entries.add(line.split("\t")[0]);
line = br.readLine();
}
Object[] tableData = new Object[entries.size()];
for (int i=0;i<entries.size(); ++i) {
tableData[i] = entries.elementAt(i);
}
br.close();
this.setValue(null, this.elementClass, tableData);
} catch (IOException e) {
JOptionPane.showMessageDialog(null,"Error!!! " +
"Could not open input file " + file.getPath() + "!");
e.printStackTrace();
}
}
/**
* Initializes the data table window.
*/
private void setupTable() {
// instantiate result table and put into scroll pane
this.dataTab= new JTable(this.dataTabModel);
this.dataTab.addFocusListener(new TableFocusListener());
this.tableScroller= new JScrollPane(this.dataTab);
this.tableScroller.setPreferredSize(new Dimension (400,400));
this.dataTab.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// add a toolbar
this.addRow= new JButton("Add Row");
this.addRow.setActionCommand("addRow");
this.addRow.addActionListener(this);
this.addRow.setSize(new Dimension(80,35));
this.addRow.setToolTipText("Appends a row to the table.");
this.delRow= new JButton("Delete Row");
this.delRow.setActionCommand("delRow");
this.delRow.addActionListener(this);
this.delRow.setSize(new Dimension(80,35));
this.delRow.setToolTipText("Deletes selected row(s) or last one.");
this.tabSave= new JButton("Save");
this.tabSave.setActionCommand("save");
this.tabSave.addActionListener(this);
this.tabSave.setSize(new Dimension(80,35));
this.tabSave.setToolTipText("Saves current table to file.");
this.tabLoad= new JButton("Load");
this.tabLoad.setActionCommand("load");
this.tabLoad.addActionListener(this);
this.tabLoad.setSize(new Dimension(80,35));
this.tabLoad.setToolTipText("Loads table from file.");
this.tabClear= new JButton("Clear");
this.tabClear.setActionCommand("clear");
this.tabClear.addActionListener(this);
this.tabClear.setSize(new Dimension(80,35));
this.tabReset= new JButton("Reset");
this.tabReset.setActionCommand("reset");
this.tabReset.addActionListener(this);
this.tabReset.setSize(new Dimension(80,35));
JButton tabClose= new JButton("Close");
tabClose.setActionCommand("close");
tabClose.addActionListener(this);
tabClose.setSize(new Dimension(80,35));
JPanel tabToolPanel= new JPanel();
tabToolPanel.setLayout(new FlowLayout());
tabToolPanel.add(this.addRow);
tabToolPanel.add(this.delRow);
tabToolPanel.add(this.tabSave);
tabToolPanel.add(this.tabLoad);
tabToolPanel.add(this.tabClear);
tabToolPanel.add(this.tabReset);
tabToolPanel.add(tabClose);
JToolBar tabTools= new JToolBar("");
tabTools.add(tabToolPanel);
this.tabPanel= new JPanel();
this.tabPanel.setLayout(new BorderLayout());
this.tabPanel.add(this.tableScroller, BorderLayout.CENTER);
this.tabPanel.add(tabTools, BorderLayout.SOUTH);
this.window.setSize(700, 250);
String paramName = "<unknown>";
if (this.descriptor != null)
paramName = "<" + this.descriptor.getLabel() + ">";
this.window.setTitle("Array data ("
+ this.elementClass.getSimpleName()
+ ") for parameter " + paramName);
this.window.add(this.tabPanel);
// ... then show it!
this.window.setVisible(false);
}
/**
* Error function displaying an error message box.
* @param errorList List of errors to format into message.
* @throws ALDDataIOProviderException Thrown on processing failures.
*/
private void errorListToMessage(LinkedList<Integer> errorList)
throws ALDDataIOProviderException {
StringBuffer errMsg = new StringBuffer();
errMsg.append(new String("Errors in table (indices = row):\n"));
for (Integer i: errorList) {
errMsg.append("Invalid entry at " + i + ".\n");
}
throw new ALDDataIOProviderException(
ALDDataIOProviderExceptionType.SYNTAX_ERROR, errMsg.toString());
}
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
// deletes all data from the table and sets row and column counts to zero
if (command.equals("clear")) {
for (int i=0;i<this.dataTabModel.getRowCount();++i) {
this.setTabEntryToDefault(i, this.elementClass);
this.entryRefList[i] = this.dataTabModel.getValueAt(i, 0);
}
}
// resets the table
else if (e.getActionCommand().equals("reset")) {
this.dataTabModel.setColumnCount(1);
this.dataTabModel.setRowCount(1);
this.entryRefList = new Object[1];
this.disableValueChecks = true;
this.setTabEntryToDefault(0, this.elementClass);
this.entryRefList[0] = this.dataTabModel.getValueAt(0, 0);
this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.descriptor));
}
// closes the GUI window
else if (e.getActionCommand().equals("close")) {
this.window.setVisible(false);
}
// saves the table data in TSV format
else if (e.getActionCommand().equals("save")) {
this.saveTable();
}
// load table data from file in TSV format
else if (e.getActionCommand().equals("load")) {
this.loadTable();
this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.descriptor));
}
// add a row
else if (command.equals("addRow")) {
this.dataTabModel.addRow(new Vector<Object>());
this.resizeTableEntries(this.dataTabModel.getRowCount());
this.setTabEntryToDefault(this.dataTabModel.getRowCount()-1,
this.elementClass);
this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.descriptor));
}
// delete row
else if (command.equals("delRow")) {
// one row should always remain...
if (this.dataTabModel.getRowCount() == 1)
return;
// get and remove all selected rows
int [] selectedRows = this.dataTab.getSelectedRows();
// if none is selected, deleted last row
if (selectedRows.length == 0) {
this.dataTabModel.removeRow(this.dataTabModel.getRowCount()-1);
this.resizeTableEntries(this.dataTabModel.getRowCount());
this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.descriptor));
return;
}
for (int c = 0; c < selectedRows.length; ++c)
this.dataTabModel.removeRow(this.dataTabModel.getRowCount()-1);
// update the reference entry table and reset correct values in
// table
int newSize = this.dataTabModel.getRowCount();
Object[] newEntries = new Object[newSize];
int newIndex = 0;
for (int i=0;i<this.entryRefList.length;++i) {
// check if row was deleted
int r = 0;
for (r = 0; r<selectedRows.length; r++) {
if (i == selectedRows[r]) {
break;
}
}
if (r == selectedRows.length) {
this.dataTabModel.setValueAt(this.entryRefList[i],newIndex,0);
newEntries[newIndex] = this.entryRefList[i];
++newIndex;
}
}
this.entryRefList = newEntries;
this.dataTab.clearSelection();
this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.descriptor));
}
}
@Override
public void tableChanged(TableModelEvent e) {
if (!this.windowHasFocus)
return;
// check validity of entries
if (this.elementClass != null) {
int r = e.getFirstRow();
int c = e.getColumn();
if (c == -1)
return;
@SuppressWarnings("unchecked")
Vector<Object> data = this.dataTabModel.getDataVector();
int rows = data.size();
if (r >= rows)
return;
@SuppressWarnings("unchecked")
String entry =
(String)((Vector<Object>)data.elementAt(r)).elementAt(0);
if ( !this.disableValueChecks
&& !this.validateEntry(this.elementClass, entry)) {
ProviderInteractionLevel plevel =
ALDDataIOManagerSwing.getInstance().
getProviderInteractionLevel();
if ( plevel.equals(ProviderInteractionLevel.ALL_ALLOWED)
|| plevel.equals(ProviderInteractionLevel.WARNINGS_ONLY)) {
Object[] options = { "OK" };
JOptionPane.showOptionDialog(null,
"Attention! You just entered an invalid value that \n" +
"cannot be cast to "
+ this.elementClass.getComponentType()
+ " , please check your input!",
"Warning", JOptionPane.DEFAULT_OPTION,
JOptionPane.WARNING_MESSAGE,
null, options, options[0]);
}
// reset table to old value
this.dataTabModel.setValueAt(this.entryRefList[r], r, c);
}
this.disableValueChecks = false;
this.entryRefList[r] = this.dataTabModel.getValueAt(r, 0);
this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this, this.descriptor));
}
}
/**
* Internal class that implements a {@link FileFilter}
* for text files where MiToBo table data is stored.
*
* @author moeller
*/
protected class DataTabFileFilter extends FileFilter {
/* (non-Javadoc)
* @see javax.swing.filechooser.FileFilter#accept(java.io.File)
*/
@Override
public boolean accept(File f) {
return (f.getName().endsWith(".txt") || f.isDirectory());
}
/* (non-Javadoc)
* @see javax.swing.filechooser.FileFilter#getDescription()
*/
@Override
public String getDescription() {
return "Alida Data Table Files (*.txt)";
}
}
/**
* Focus listener to commit edits if table looses the focus.
* @author moeller
*/
protected class TableFocusListener implements FocusListener {
@Override
public void focusGained(FocusEvent e) {
ArrayConfigWindow.this.windowHasFocus = true;
// nothing to do here, just ignored
}
@Override
public void focusLost(FocusEvent e) {
ArrayConfigWindow.this.windowHasFocus = false;
if ( ArrayConfigWindow.this.dataTab == null
|| ArrayConfigWindow.this.dataTab.getCellEditor() == null)
return;
ArrayConfigWindow.this.dataTab.getCellEditor().stopCellEditing();
ArrayConfigWindow.this.fireALDSwingValueChangeEvent(
new ALDSwingValueChangeEvent(this,
ArrayConfigWindow.this.descriptor));
}
}
}
}