diff --git a/src/eva2/gui/EvATreeSelectionListener.java b/src/eva2/gui/EvATreeSelectionListener.java index cbf1ff0e..c2e62b4a 100644 --- a/src/eva2/gui/EvATreeSelectionListener.java +++ b/src/eva2/gui/EvATreeSelectionListener.java @@ -3,6 +3,7 @@ package eva2.gui; import java.awt.Component; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyEditor; import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; @@ -19,7 +20,7 @@ import javax.swing.tree.TreePath; * */ public class EvATreeSelectionListener implements TreeSelectionListener, PropertyChangeListener { - private GenericObjectEditor goe=null; + private PropertyEditor goe=null; private EvATreeNode root = null; private JTree jtree = null; public static final boolean TRACE = true; @@ -32,7 +33,7 @@ public class EvATreeSelectionListener implements TreeSelectionListener, Property * @param goEditor the editor containing the parameter panel * @param jt the GUI view of the tree */ - public EvATreeSelectionListener(EvATreeNode rootNode, GenericObjectEditor goEditor, JTree jt) { + public EvATreeSelectionListener(EvATreeNode rootNode, PropertyEditor goEditor, JTree jt) { goe = goEditor; root = rootNode; jtree = jt; diff --git a/src/eva2/gui/GenericArrayEditor.java b/src/eva2/gui/GenericArrayEditor.java index 4b8c8998..7644611d 100644 --- a/src/eva2/gui/GenericArrayEditor.java +++ b/src/eva2/gui/GenericArrayEditor.java @@ -22,32 +22,38 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Insets; +import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyEditor; import java.lang.reflect.Array; +import java.util.LinkedList; +import java.util.List; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; +import javax.swing.ListModel; import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import eva2.tools.EVAHELP; -import eva2.tools.SelectedTag; import eva2.tools.SerializedObject; /*==========================================================================* * CLASS DECLARATION @@ -70,10 +76,22 @@ implements PropertyEditor { private PropertySelectableList selectableList = null; /** Click this to delete the selected array values */ private JButton m_DeleteBut = new JButton("Delete"); - /** Click to add the current object configuration to the array */ + /** list of additional buttons above the list */ + private List m_AdditionalUpperButtonList = new LinkedList(); + /** list of additional buttons below the list */ + private List m_AdditionalLowerButtonList = new LinkedList(); + + private JPanel additionalCenterPane = null; + + private List m_popupItemList = new LinkedList(); private JButton m_AddBut = new JButton("Add"); private JButton m_SetBut = new JButton("Set"); private JButton m_SetAllBut = new JButton("Set all"); + + private boolean withAddButton = true; + private boolean withSetButton = true; + private boolean withDeleteButton = true; + private Component m_View = null; /** Listens to buttons being pressed and taking the appropriate action */ private ActionListener m_InnerActionListener = @@ -100,7 +118,7 @@ implements PropertyEditor { m_ElementList.setModel(m_ListModel); } - if (selectableList!=null) selectableList.setObjects(modelToArray(m_ListModel)); + if (selectableList!=null) selectableList.setObjects(modelToArray(selectableList.getObjects(), m_ListModel)); m_Support.firePropertyChange("", null, null); } if (m_ElementList.getSelectedIndex() == -1) { @@ -121,7 +139,7 @@ implements PropertyEditor { m_ListModel.addElement(addObj); } m_ElementList.setModel(m_ListModel); - if (selectableList!=null) selectableList.setObjects(modelToArray(m_ListModel)); + if (selectableList!=null) selectableList.setObjects(modelToArray(selectableList.getObjects(), m_ListModel)); m_Support.firePropertyChange("", null, null); } catch (Exception ex) { JOptionPane.showMessageDialog(GenericArrayEditor.this,"Could not create an object copy",null,JOptionPane.ERROR_MESSAGE); @@ -151,8 +169,15 @@ implements PropertyEditor { } }; - private Object[] modelToArray(DefaultListModel listModel) { - Object[] os= new Object[listModel.size()]; + public void setAdditionalCenterPane(JPanel panel) { + this.additionalCenterPane = panel; + } + + private Object[] modelToArray(Object[] origArray, DefaultListModel listModel) { + Class objClass = origArray.getClass().getComponentType(); + Object[] os = (Object[]) java.lang.reflect.Array.newInstance(objClass, listModel.size()); + +// Object[] os= new Object[listModel.size()]; for (int i=0; i=0 && (list.getCellBounds(index, index).contains(e.getPoint()))) { + PropertyPanel propPanel=null; + Component comp = gae.m_View; + if (comp instanceof PropertyPanel ) propPanel = (PropertyPanel) comp; + else System.err.println("Error, invalid property panel in " + this.getClass()); + ListModel dlm = list.getModel(); + Object item = dlm.getElementAt(index); + list.ensureIndexIsVisible(index); +// System.out.println(e); +// System.out.println("Double clicked on " + item); + propPanel.getEditor().setValue(item); + propPanel.showDialog(e.getXOnScreen(), e.getYOnScreen()); + propPanel=null; +// int x = getLocationOnScreen().x; +// int y = getLocationOnScreen().y; + +// if (m_PropertyDialog == null) +// m_PropertyDialog = new PropertyDialog(gae.m_ElementEditor, EVAHELP.cutClassName(gae.m_ElementEditor.getClass().getName()) , x, y); +// else { +// m_PropertyDialog.updateFrameTitle(gae.m_ElementEditor); +// m_PropertyDialog.set +// m_PropertyDialog.setVisible(false); +// m_PropertyDialog.setExtendedState(JFrame.NORMAL); +// m_PropertyDialog.setVisible(true); +// m_PropertyDialog.requestFocus(); +// } + + } + } + } + } /* This class handles the creation of list cell renderers from the * property editors. */ @@ -232,7 +311,7 @@ implements PropertyEditor { ((GenericObjectEditor) e).setClassType(m_ValueClass); } e.setValue(value); - return new JPanel() { + JPanel cellPanel = new JPanel() { // return new JCheckBox("", isSelected) { // public void paintComponent(Graphics g) { // String name = (String)BeanInspector.callIfAvailable(value, "getName", new Object[]{}); @@ -258,6 +337,7 @@ implements PropertyEditor { return newPref; } }; + return cellPanel; } catch (Exception ex) { return null; } @@ -273,7 +353,9 @@ implements PropertyEditor { private void updateEditorType(Object obj) { // Determine if the current object is an array - m_ElementEditor = null; m_ListModel = null; + m_ElementEditor = null; + m_ListModel = null; + m_View = null; removeAll(); if ((obj != null) && (obj.getClass().isArray() || (obj instanceof PropertySelectableList))) { @@ -341,19 +423,40 @@ implements PropertyEditor { // panel.add(view, BorderLayout.CENTER); // panel.add(m_AddBut, BorderLayout.EAST); // JPanel buttonPanel=new JPanel(new FlowLayout()); - JPanel combinedPanel = new JPanel(new GridLayout(1,3)); - combinedPanel.add(m_View ); - combinedPanel.add(m_AddBut); - combinedPanel.add(m_SetBut); - combinedPanel.add(m_SetAllBut); - add(combinedPanel, BorderLayout.NORTH); - add(new JScrollPane(m_ElementList), BorderLayout.CENTER); - add(m_DeleteBut, BorderLayout.SOUTH); + + if (withAddButton && !(m_AdditionalUpperButtonList.contains(m_AddBut))) m_AdditionalUpperButtonList.add(m_AddBut); + if (withSetButton && !(m_AdditionalUpperButtonList.contains(m_SetBut))) m_AdditionalUpperButtonList.add(m_SetBut); + if (withSetButton && !(m_AdditionalUpperButtonList.contains(m_SetAllBut))) m_AdditionalUpperButtonList.add(m_SetAllBut); + + JPanel combiUpperPanel = new JPanel(getButtonLayout(1, m_AdditionalUpperButtonList)); + combiUpperPanel.add(m_View ); + + for (JButton but : m_AdditionalUpperButtonList) { + combiUpperPanel.add(but); + } + add(combiUpperPanel, BorderLayout.NORTH); + if (additionalCenterPane==null) add(new JScrollPane(m_ElementList), BorderLayout.CENTER); + else { + JPanel centerPane=new JPanel(); + centerPane.setLayout(new GridLayout(2, 1)); + centerPane.add(new JScrollPane(m_ElementList)); + centerPane.add(additionalCenterPane); + add(centerPane, BorderLayout.CENTER); + } + + if (withDeleteButton && !m_AdditionalLowerButtonList.contains(m_DeleteBut)) m_AdditionalLowerButtonList.add(m_DeleteBut); + JPanel combiLowerPanel = new JPanel(getButtonLayout(0, m_AdditionalLowerButtonList)); + for (JButton but : m_AdditionalLowerButtonList) { + combiLowerPanel.add(but); + } + add(combiLowerPanel, BorderLayout.SOUTH); m_ElementEditor.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { repaint(); } }); + + addPopupMenu(); } catch (Exception ex) { System.err.println(ex.getMessage()); ex.printStackTrace(); @@ -367,6 +470,71 @@ implements PropertyEditor { m_Support.firePropertyChange("", null, null); validate(); } + + /** + * Make a fitting grid layout for a list of buttons. An additional offset may be given + * if further components should be added besides the buttons. + * + * @param additionalOffset + * @param bList + * @return + */ + private LayoutManager getButtonLayout(int additionalOffset, List bList) { + int lines = 1+((bList.size()+additionalOffset-1)/3); + int cols = 3; + return new GridLayout(lines, cols); + } + + public void removeUpperActionButton(String text) { + removeActionButton(m_AdditionalUpperButtonList, text); + } + + public void removeLowerActionButton(String text) { + removeActionButton(m_AdditionalLowerButtonList, text); + } + + protected void removeActionButton(List bList, String text) { + JButton but = null; + for (JButton jb : bList) { + if (text.equals(jb.getText())) { + but = jb; + break; + } + } + if (but!=null) bList.remove(but); + } + + public void addUpperActionButton(String text, ActionListener al) { + addActionButton(m_AdditionalUpperButtonList, text, al); + } + + /** + * Wrap an action listener such that the selection state will always be up to date + * in the selectableList (if it exists). + * @param al + * @return + */ + private ActionListener makeSelectionKnownAL(final ActionListener al) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (selectableList!=null) { + selectableList.setSelectionByIndices(m_ElementList.getSelectedIndices()); + } + al.actionPerformed(e); + } + }; + } + + public void addLowerActionButton(String text, ActionListener al) { + addActionButton(m_AdditionalLowerButtonList, text, al); + } + + public void addActionButton(List bList, String text, ActionListener al) { + JButton but = new JButton(text); + but.addActionListener(makeSelectionKnownAL(al)); + bList.add(but); + } + /** * Sets the current object array. * @@ -377,6 +545,21 @@ implements PropertyEditor { updateEditorType(o); } + /** + * Select all items. If all are selected, then deselect all items. + */ + public void selectDeselectAll() { + if (areAllSelected()) m_ElementList.getSelectionModel().clearSelection(); + else m_ElementList.setSelectionInterval(0, m_ElementList.getModel().getSize()-1); + } + + public boolean areAllSelected() { + for (int i=0; i0) { + m_ElementList.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (selectableList!=null) { + selectableList.setSelectionByIndices(m_ElementList.getSelectedIndices()); + } + if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) { + // do nothing + } else { // right click released, so show popup + JPopupMenu popupMenu = new JPopupMenu(); + for (JMenuItem item : m_popupItemList) popupMenu.add(item); + popupMenu.show(GenericArrayEditor.this, e.getX(), e.getY()); + } + } + }); + } + } + + /** + * Create a menu item with given title and listener, add it to the menu and + * return it. It may be enabled or disabled. + * + * @param menu + * @param title + * @param aListener + * @param enabled + * @return + */ + private JMenuItem createMenuItem(String title, boolean enabled, + ActionListener aListener) { + JMenuItem item = new JMenuItem(title); + // if (bgColor!=null) item.setForeground(bgColor); + item.addActionListener(aListener); + item.setEnabled(enabled); + return item; + } + /** * Supposedly returns an initialization string to create a classifier * identical to the current one, including it's state, but this doesn't @@ -486,30 +712,53 @@ implements PropertyEditor { /** * */ - public static void main(String [] args) { - try { - java.beans.PropertyEditorManager.registerEditor(SelectedTag.class,TagEditor.class); - java.beans.PropertyEditorManager.registerEditor(int [].class,GenericArrayEditor.class); - java.beans.PropertyEditorManager.registerEditor(double [].class,GenericArrayEditor.class); - GenericArrayEditor editor = new GenericArrayEditor(); - - - int[] initial = { 3,45, 7}; - editor.setValue(initial); - PropertyDialog pd = new PropertyDialog(editor,EVAHELP.cutClassName(editor.getClass().getName()) - , 100, 100); -// pd.setSize(200,200); - pd.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - System.exit(0); - } - }); - editor.setValue(initial); - //ce.validate(); - } catch (Exception ex) { - ex.printStackTrace(); - System.err.println(ex.getMessage()); - } +// public static void main(String [] args) { +// try { +// java.beans.PropertyEditorManager.registerEditor(SelectedTag.class,TagEditor.class); +// java.beans.PropertyEditorManager.registerEditor(int [].class,GenericArrayEditor.class); +// java.beans.PropertyEditorManager.registerEditor(double [].class,GenericArrayEditor.class); +// GenericArrayEditor editor = new GenericArrayEditor(); +// +// +// int[] initial = { 3,45, 7}; +// editor.setValue(initial); +// PropertyDialog pd = new PropertyDialog(editor,EVAHELP.cutClassName(editor.getClass().getName()) +// , 100, 100); +//// pd.setSize(200,200); +// pd.addWindowListener(new WindowAdapter() { +// public void windowClosing(WindowEvent e) { +// System.exit(0); +// } +// }); +// editor.setValue(initial); +// //ce.validate(); +// } catch (Exception ex) { +// ex.printStackTrace(); +// System.err.println(ex.getMessage()); +// } +// } + public boolean isWithAddButton() { + return withAddButton; + } + public void setWithAddButton(boolean withAddButton) { + this.withAddButton = withAddButton; + } + public boolean isWithSetButton() { + return withSetButton; + } + public void setWithSetButton(boolean withSetButton) { + this.withSetButton = withSetButton; + } + + public boolean isWithDeleteButton() { + return withDeleteButton; + } + public void setWithDeleteButton(boolean wB) { + this.withDeleteButton = wB; + } + + public void removeNotify() { + super.removeNotify(); } } diff --git a/src/eva2/gui/JParaPanel.java b/src/eva2/gui/JParaPanel.java index e8fe3e75..700e9068 100644 --- a/src/eva2/gui/JParaPanel.java +++ b/src/eva2/gui/JParaPanel.java @@ -15,6 +15,7 @@ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagLayout; +import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; import java.io.Serializable; @@ -23,13 +24,15 @@ import javax.swing.Box; import javax.swing.JComponent; import javax.swing.JPanel; +import eva2.server.stat.EvAJobList; + public class JParaPanel implements Serializable, PanelMaker { public static boolean TRACE = false; protected String m_Name = "undefined"; protected Object m_LocalParameter; protected Object m_ProxyParameter; - protected GenericObjectEditor m_Editor; + protected PropertyEditor m_Editor; private JPanel m_Panel; public JParaPanel() { @@ -49,11 +52,14 @@ public class JParaPanel implements Serializable, PanelMaker { //m_Panel.setPreferredSize(new Dimension(200, 200)); // MK: this was evil, killing all the auto-layout mechanisms PropertyEditorProvider.installEditors(); - m_Editor = new GenericObjectEditor(); - ((GenericObjectEditor) (m_Editor)).setClassType(m_LocalParameter. - getClass()); - ((GenericObjectEditor) (m_Editor)).setValue(m_LocalParameter); - ((GenericObjectEditor) (m_Editor)).disableOKCancel(); + if (m_LocalParameter instanceof EvAJobList) { + m_Editor = EvAJobList.makeEditor(m_Panel, (EvAJobList)m_LocalParameter); + } else { + m_Editor = new GenericObjectEditor(); + ((GenericObjectEditor) (m_Editor)).setClassType(m_LocalParameter.getClass()); + ((GenericObjectEditor) (m_Editor)).setValue(m_LocalParameter); + ((GenericObjectEditor) (m_Editor)).disableOKCancel(); + } m_Panel.setLayout(new BorderLayout()); m_Panel.add(m_Editor.getCustomEditor(), BorderLayout.CENTER); @@ -71,7 +77,7 @@ public class JParaPanel implements Serializable, PanelMaker { return m_Name; } - public GenericObjectEditor getEditor() { + public PropertyEditor getEditor() { return m_Editor; } diff --git a/src/eva2/gui/PropertyPanel.java b/src/eva2/gui/PropertyPanel.java index 4a34e9a7..0194ec6a 100644 --- a/src/eva2/gui/PropertyPanel.java +++ b/src/eva2/gui/PropertyPanel.java @@ -45,39 +45,52 @@ public class PropertyPanel extends JPanel { addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent evt) { if (m_PropertyEditor.getValue() != null) { - if (m_PropertyDialog == null) { - int x = getLocationOnScreen().x; - int y = getLocationOnScreen().y; - m_PropertyDialog = new PropertyDialog(m_PropertyEditor, EVAHELP.cutClassName(m_PropertyEditor.getClass().getName()) , x, y); - } - else { - m_PropertyDialog.updateFrameTitle(m_PropertyEditor); -// System.out.println("" + BeanInspector.toString(m_PropertyDialog)); - m_PropertyDialog.setVisible(false); - m_PropertyDialog.setExtendedState(JFrame.NORMAL); - m_PropertyDialog.setVisible(true); - m_PropertyDialog.requestFocus(); -// System.out.println("" + BeanInspector.toString(m_PropertyDialog)); -// System.out.println("Aft: " + m_PropertyDialog.isShowing() + " " + m_PropertyDialog.isVisible() + " " + m_PropertyDialog.isActive() + " " + m_PropertyDialog.isFocused()); - } + showDialog(getLocationOnScreen().x, getLocationOnScreen().y); } } + }); Dimension newPref = getPreferredSize(); newPref.height = getFontMetrics(getFont()).getHeight() * 6 / 4; //6 / 4; newPref.width = newPref.height * 6; //5 setPreferredSize(newPref); } + + public void showDialog(int initX, int initY) { + if (m_PropertyDialog == null) { +// int x = getLocationOnScreen().x; +// int y = getLocationOnScreen().y; + m_PropertyDialog = new PropertyDialog(m_PropertyEditor, EVAHELP.cutClassName(m_PropertyEditor.getClass().getName()) , initX, initY); + m_PropertyDialog.setPreferredSize(new Dimension(500,300)); + } + else { + m_PropertyDialog.updateFrameTitle(m_PropertyEditor); +// System.out.println("" + BeanInspector.toString(m_PropertyDialog)); + m_PropertyDialog.setVisible(false); + m_PropertyDialog.setExtendedState(JFrame.NORMAL); + m_PropertyDialog.setVisible(true); + m_PropertyDialog.requestFocus(); +// System.out.println("" + BeanInspector.toString(m_PropertyDialog)); +// System.out.println("Aft: " + m_PropertyDialog.isShowing() + " " + m_PropertyDialog.isVisible() + " " + m_PropertyDialog.isActive() + " " + m_PropertyDialog.isFocused()); + } + } + /** * */ public void removeNotify() { if (m_PropertyDialog != null) { - //System.out.println(" m_PropertyDialog.dispose();"); - m_PropertyDialog.dispose(); +// System.out.println(" m_PropertyDialog.dispose();"); +// System.out.println(m_PropertyDialog.isActive()); +// System.out.println(m_PropertyDialog.isVisible()); +// System.out.println(m_PropertyDialog.isValid()); +// System.out.println(m_PropertyDialog.isDisplayable()); +// if (m_PropertyDialog.isDisplayable()) m_PropertyDialog.dispose(); // this caused a deadlock! +// m_PropertyDialog.dispose(); // this also caused a deadlock! m_PropertyDialog = null; } } + /** * */ diff --git a/src/eva2/gui/PropertySelectableList.java b/src/eva2/gui/PropertySelectableList.java index fc3d487f..6f8a68e7 100644 --- a/src/eva2/gui/PropertySelectableList.java +++ b/src/eva2/gui/PropertySelectableList.java @@ -12,9 +12,9 @@ import java.beans.PropertyChangeSupport; */ public class PropertySelectableList implements java.io.Serializable { - private T[] m_Objects; - private boolean[] m_Selection; - private PropertyChangeSupport m_Support = new PropertyChangeSupport(this); + protected T[] m_Objects; + protected boolean[] m_Selection; + private transient PropertyChangeSupport m_Support = new PropertyChangeSupport(this); // public PropertySelectableList() { // } @@ -44,10 +44,42 @@ public class PropertySelectableList implements java.io.Serializable { m_Support.firePropertyChange("PropertySelectableList", null, this); } + public void setObjects(T[] o, boolean[] selection) { + this.m_Objects = o; + this.m_Selection = selection; + if (o.length != selection.length) throw new RuntimeException("Error, mismatching length of arrays in " + this.getClass()); + m_Support.firePropertyChange("PropertySelectableList", null, this); + } + public T[] getObjects() { return this.m_Objects; } - + + /** + * Returns the elements represented by this list where only the selected elements are non-null. + * @return + */ + public T[] getSelectedObjects() { + T[] selObjects = getObjects().clone(); + for (int i=0; i toRemove = new LinkedList(); + for (InterfaceStatisticsListener l : dataListeners) { + boolean rm = l.notifyMultiRunFinished(currentStatHeader, finalObjectData); + if (rm) toRemove.add(l); + } + for (InterfaceStatisticsListener l : toRemove) dataListeners.remove(l); + } + } + /** * Notify listeners on the start and stop of a run. * @@ -155,10 +167,10 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter currentStatHeader, currentStatMetaInfo); } else { l.notifyRunStopped(optRunsPerformed, normal); - if (optRunsPerformed > 1) { +// if (optRunsPerformed > 1) { l.finalMultiRunResults(currentStatHeader, finalObjectData); - } +// } } } } @@ -369,6 +381,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter } if (optRunsPerformed >= m_StatsParams.getMultiRuns()) { finalizeOutput(); + fireDataListenersFinalize(); } } diff --git a/src/eva2/server/stat/EvAJob.java b/src/eva2/server/stat/EvAJob.java new file mode 100644 index 00000000..082d9743 --- /dev/null +++ b/src/eva2/server/stat/EvAJob.java @@ -0,0 +1,243 @@ +package eva2.server.stat; + +import java.io.Serializable; +import java.util.List; + +import eva2.gui.BeanInspector; +import eva2.server.go.InterfaceGOParameters; +import eva2.tools.StringSelection; + +/** + * An EvAJob is a set of optimization parameters and potential results from the statistics class. + * Each job has a unique ID and may have been completely finished or not. Once finished, the + * framework should guarantee that the job is removed as a statistics listener. + * + * A job contains data fields of a multi-run experiment and header strings describing the data. + * + * @author mkron + * + */ +public class EvAJob implements Serializable, InterfaceStatisticsListener { + private static final boolean TRACE = false; + + private InterfaceGOParameters params = null; + private String[] fieldHeaders = null; + private List multiRunFinalObjectData = null; + private int jobID=0; + private static int jobIDCounter=0; + private int numRuns = 0; + private boolean lastRunIncompl = false; +// private boolean jobFinished = false; + private StateEnum state = StateEnum.idle; + + private enum StateEnum { running, idle, complete, incomplete}; + + public EvAJob() { + jobID=jobIDCounter; + jobIDCounter++; + } + + public EvAJob(InterfaceGOParameters params, InterfaceStatistics sts) { + this(); + this.params = params; + sts.addDataListener(this); + } + + /** + * Clear the job by resetting its state and clearing all data (which will be lost!). + * The parameters of course remain. + */ + public void resetJob() { + numRuns = 0; + state=StateEnum.idle; + lastRunIncompl=false; +// jobFinished=false; + fieldHeaders=null; + multiRunFinalObjectData=null; + } + + public InterfaceGOParameters getParams() { + return params; + } + + /** + * Set the GO parameters for this instance. + * + * @param params + */ + public void setParams(InterfaceGOParameters params) { + // how should this be treated? In case the run is already finished, changing + // the parameters will be evil, so avoid that case. + if (state==StateEnum.complete) { + System.err.println("Warning, ignoring changed parameters for finished job!"); + } else this.params = params; + } + + public String getName() { + if (params==null) return "Invalid Job ("+jobID+")"; + else { + String name=getStateTag(); + name = name+" Job ("+jobID+"), "; // +params.getName(); + name=name+params.getOptimizer().getName()+"/"+params.getProblem().getName(); +// name=name+( (onlineData==null) ? ", empty" : (onlineData.size()+" records")); + name=name+(", " + numRuns + " runs"); + if (fieldHeaders!=null) name=name+(", " + fieldHeaders.length + " fields"); +// if (jobFinished) name=name+", finished"; +// if (lastRunIncompl) name=name+", incomplete"; + return name; + } + } + + private String getStateTag() { + String tag = null; + switch(state) { + case complete: tag="*"; break; + case incomplete: tag="?"; break; + case idle: tag="."; break; + case running: tag="!"; break; + } + + tag = tag+numRuns+" "; + return tag; +// if (isFinishedAndComplete()) return "*"; +// else if (jobFinished && !lastRunIncompl) return "?"; +// else if (!jobFinished && lastRunIncompl) return "-"; +// else if (!jobFinished && !lastRunIncompl) return "O"; +// else { +// System.err.println("Invalid state of job " + this.toString()); +// return "?"; +// } + } + + public String globalInfo() { + return "Job: " + BeanInspector.niceToString(params); + } + + public boolean isFinishedAndComplete() { +// return (jobFinished && !lastRunIncompl); + return (state==StateEnum.complete) && !lastRunIncompl; + } + + public String[] getFieldHeaders() { + return fieldHeaders; + } + + public List getJobData() { + return multiRunFinalObjectData; + } + + public InterfaceGOParameters getGOParams() { + return params; + } + + public void finalMultiRunResults(String[] header, + List multiRunFinalObjDat) { + fieldHeaders=header; + multiRunFinalObjectData = multiRunFinalObjDat; + } + + public void notifyGenerationPerformed(String[] header, + Object[] statObjects, Double[] statDoubles) { + fieldHeaders=header; + if (state!=StateEnum.running) throw new RuntimeException("Sent data to job with invalid state!"); +// if (onlineData==null) onlineData = new ArrayList(10); +// onlineData.add(statObjects); + } + + public void notifyRunStarted(int runNumber, int plannedMultiRuns, + String[] header, String[] metaInfo) { + state=StateEnum.running; +// onlineData = null; +// multiRunFinalObjectData = null; + } + + public void notifyRunStopped(int runsPerformed, boolean completedLastRun) { + numRuns=runsPerformed; + lastRunIncompl = !completedLastRun; + if (TRACE) System.out.println("EvAJob.notifyRunStopped, " + runsPerformed + " " + completedLastRun); + } + + public boolean notifyMultiRunFinished(String[] header, List multiRunFinalObjDat) { + fieldHeaders=header; + multiRunFinalObjectData = multiRunFinalObjDat; + if (lastRunIncompl) state=StateEnum.incomplete; + else state=StateEnum.complete; +// jobFinished=true; + if (TRACE) System.out.println("multi run finished!"); + return true; + } + + /** + * Retrieve the index of a data field within the data lines. + * Returns -1 if the field has not been found. + * + * @param field + * @return + */ + public int getFieldIndex(String field) { + if (fieldHeaders!=null) { + for (int i=0; i=0) { + double[] data = new double[getNumRuns()]; + for (int i=0; i=0) { + Object[] data = new Object[getNumRuns()]; + for (int i=0; i implements Serializable, InterfaceTextListener { + List listeners = null; + + private ModuleAdapter module = null; +// private static transient LogPanel logPanel = null; +// private static transient JFrame statsFrame = null; + + public EvAJobList(EvAJob[] initial) { + super(initial); + } + + public String getName() { + return "Job Set"; + } + + public String globalInfo() { + return "Display a set of jobs consisting of a multi-run experiment."; + } + + /** + * This adds a new job to the list. + * + * @param params + * @param stats + */ + public EvAJob addJob(InterfaceGOParameters params, AbstractStatistics stats) { + EvAJob job = new EvAJob((InterfaceGOParameters)Serializer.deepClone(params), stats); + stats.addDataListener(job); + addJob(job, true); + return job; + } + + private void addJob(EvAJob j, boolean selected) { + EvAJob[] curArr = getObjects(); + EvAJob[] newArr = null; + boolean[] newSelection = null; + if (curArr!=null && curArr.length>0) { + newArr = new EvAJob[curArr.length + 1]; + newSelection = new boolean[newArr.length]; + System.arraycopy(curArr, 0, newArr, 0, curArr.length); + System.arraycopy(m_Selection, 0, newSelection, 0, curArr.length); + } else { + newArr = new EvAJob[1]; + newSelection = new boolean[1]; + } + newSelection[newArr.length-1]=selected; + newArr[newArr.length-1] = j; + setObjects(newArr, newSelection); + } + + /** + * Return the last job in the list, which is also the last one added. + * + * @return + */ + public EvAJob lastJob() { + EvAJob[] curArr = getObjects(); + if (curArr!=null && curArr.length>0) return curArr[curArr.length-1]; + else return null; + } + + /** + * Return a list of the currently selected jobs. + * + * @return + */ + public ArrayList getSelectedJobs() { + EvAJob[] selected = getSelectedObjects(); + ArrayList l = new ArrayList(); + for (EvAJob j : selected) { + if (j!=null) l.add(j); + } + return l; + } + + public boolean saveSelectedJobs(Component parentComponent) { + EvAJob[] selected = getSelectedObjects(); + if (selected!=null && (selected.length>0)) { + JFileChooser fc = new JFileChooser(); + fc.setName("Select a directory to save jobs to..."); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnVal = fc.showSaveDialog(parentComponent); + if (returnVal==JFileChooser.APPROVE_OPTION) { + File sFile = fc.getSelectedFile(); + if (sFile.exists()) { + for (EvAJob job : selected) { + if (!FileTools.saveObjectToFolder(job, sFile, false, parentComponent)) { + System.err.println("Error on saving jobs..."); + return false; + } + } + } else return false; // invalid folder chosen + } else return false; // user break + } + return true; + } + + /** + * Search for a job in the list which has the given parameter structure assigned. + * This is tested by reference, so the exact same instance of InterfaceGOParameters + * must be known. If no matching job is found, null is returned. + * @param params + * @return + */ + public EvAJob getJobOf(InterfaceGOParameters params) { + for (EvAJob job : getObjects()) { + if (job.getGOParams()==params) return job; + } + return null; + } + +// private static List getActionButtons(final EvAJobList jobList, final GenericArrayEditor edi) { +// LinkedList buts = new LinkedList(); +// buts.add(getRunButton(jobList, edi)); +// return buts; +// } + +// private static JButton getRunButton(final EvAJobList jobList, final GenericArrayEditor edi) { +// JButton runBut = new JButton("Exec"); +// runBut.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// System.out.println("PING!"); +// System.out.println(BeanInspector.toString(edi.getSelectedIndices())); +// EvAStatisticalEvaluation.evaluate((InterfaceTextListener)jobList, jobList.getObjects(), edi.getSelectedIndices()); +// System.out.println(BeanInspector.toString(EvAStatisticalEvaluation.statsParams.getPairedStats().getSelected())); +// } +// }); +// return runBut; +// } + + /** + * Create a customized editor for the job list based on an array editor. + * + * @param jobList + * @return + */ + public static PropertyEditor makeEditor(final Component parent, final EvAJobList jobList) { + final GenericArrayEditor edi = new GenericArrayEditor(); + edi.setWithAddButton(false); + edi.setWithSetButton(false); + ActionListener al=new ActionListener() { + public void actionPerformed(ActionEvent e) { +// System.out.println("PING!"); +// System.out.println(BeanInspector.toString(edi.getSelectedIndices())); + EvAStatisticalEvaluation.evaluate((InterfaceTextListener)jobList, jobList.getObjects(), edi.getSelectedIndices(), + (StatsOnSingleDataSetEnum[])EvAStatisticalEvaluation.statsParams.getOneSampledStats().getSelectedEnum(StatsOnSingleDataSetEnum.values()), + (StatsOnTwoSampledDataEnum[])EvAStatisticalEvaluation.statsParams.getTwoSampledStats().getSelectedEnum(StatsOnTwoSampledDataEnum.values())); +// System.out.println(BeanInspector.toString(EvAStatisticalEvaluation.statsParams.getPairedStats().getSelected())); + } +// public void actionPerformed(ActionEvent e) { +// if (statsFrame ==null) { +// statsFrame = new JEFrame("EvA2 Statistics Evaluation", true); +// JPanel tmpPan = createStatsPanel(jobList, edi); +// statsFrame.getContentPane().add(tmpPan); +// } +// if (!statsFrame.isVisible()) { +// statsFrame.pack(); +// statsFrame.validate(); +// statsFrame.setVisible(true); +// } else statsFrame.requestFocus(); +// } + }; + ActionListener sl=new ActionListener() { + public void actionPerformed(ActionEvent e) { + edi.selectDeselectAll(); + } + }; + ActionListener sal=new ActionListener() { + public void actionPerformed(ActionEvent e) { + jobList.saveSelectedJobs(edi); + } + }; + edi.addUpperActionButton("(De-)Sel. all", sl); + edi.addUpperActionButton("Test Stats", al); + edi.addLowerActionButton("Save selected", sal); + +// edi.addPopupItem("Reset selected", getClearSelectedActionListener(parent, jobList)); // this option does not make much sense - instead of deleting data, taking over the settings for a new run is more plausible + edi.addPopupItem("Reuse as current settings", getReuseActionListener(parent, jobList)); + edi.setAdditionalCenterPane(createStatsPanel(jobList, edi)); + edi.setValue(jobList); + return edi; + } + + private static JPanel createStatsPanel(final EvAJobList jobList, final GenericArrayEditor edi) { +// if (logPanel != null) System.err.println("Error: logPanel should be null!!!"); +// logPanel = new LogPanel(); +// EvAStatisticalEvaluation.statsParams.setGenericAdditionalButtons(getActionButtons(lPan, jobList, edi)); + JParaPanel pan = new JParaPanel(EvAStatisticalEvaluation.statsParams, "Statistics"); + // GOEPanel pan = new GOEPanel(EvAStatisticalEvaluation.selectedSingleStats, jobList, null, new GenericObjectEditor()); + JComponent paraPan = pan.makePanel(); + JPanel tmpPan = new JPanel(); +// tmpPan.setPreferredSize(new Dimension(1200,1200)); + tmpPan.add(paraPan); +// tmpPan.add(logPanel); + return tmpPan; + } + +// public static void main(String[] args) { +// JEFrame frm = new JEFrame("Test", true); +// EvAJobList jl = new EvAJobList(new EvAJob[]{}); +// frm.add((GenericArrayEditor)makeEditor(null, jl)); +// frm.pack(); +// frm.setVisible(true); +// } + + private static ActionListener getReuseActionListener(final Component parent, final EvAJobList jobList) { + ActionListener al = new ActionListener() { + public void actionPerformed(ActionEvent e) { + List jobs = jobList.getSelectedJobs(); + if (jobs.size()==1) { + EvAJob job = jobs.get(0); + AbstractGOParameters curParams = (AbstractGOParameters)((AbstractModuleAdapter)jobList.module).getGOParameters(); + curParams.setSameParams((AbstractGOParameters) job.getGOParams()); + ((GenericModuleAdapter)jobList.module).setGOParameters(curParams); + ((GenericModuleAdapter)jobList.module).getStatistics().getStatisticsParameter().setMultiRuns(job.getNumRuns()); + ((GenericModuleAdapter)jobList.module).getStatistics().getStatisticsParameter().setFieldSelection(job.getFieldSelection(((GenericModuleAdapter)jobList.module).getStatistics().getStatisticsParameter().getFieldSelection())); + } else JOptionPane.showMessageDialog(parent, "Select exactly one job to reuse!", "Error", JOptionPane.ERROR_MESSAGE); + } + }; + return al; + } + + private static ActionListener getClearSelectedActionListener(final Component parent, final EvAJobList jobList) { + ActionListener al = new ActionListener() { + public void actionPerformed(ActionEvent e) { + List jobs = jobList.getSelectedJobs(); + for (EvAJob j : jobs) j.resetJob(); + } + }; + return al; + } + + /** + * Link a processor to the job list for re-scheduling jobs. + * @param processor + */ + public void setModule(ModuleAdapter mod) { + module = mod; + } + + public void addTextListener(InterfaceTextListener tListener) { + if (listeners==null) listeners = new LinkedList(); + if (!listeners.contains(tListener)) listeners.add(tListener); + } + + public boolean removeTextListener(InterfaceTextListener tListener) { + if (listeners!=null) { + return listeners.remove(tListener); + } else return false; + } + + @Override + public void print(String str) { + if (listeners!=null) for (InterfaceTextListener lst : listeners) { + lst.print(str); + } + } + + @Override + public void println(String str) { + if (listeners!=null) for (InterfaceTextListener lst : listeners) { + lst.println(str); + } + } +} diff --git a/src/eva2/server/stat/EvAStatisticalEvaluation.java b/src/eva2/server/stat/EvAStatisticalEvaluation.java new file mode 100644 index 00000000..6b25c5f6 --- /dev/null +++ b/src/eva2/server/stat/EvAStatisticalEvaluation.java @@ -0,0 +1,326 @@ +package eva2.server.stat; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import eva2.gui.BeanInspector; +import eva2.tools.ReflectPackage; +import eva2.tools.math.Mathematics; + +/** + * Do some statistical tests on a set of job results. Note that the plausibility (comparability of the + * jobs) is not tested here. + * + * @author mkron + * + */ +public class EvAStatisticalEvaluation { + public static final boolean TRACE=false; +// public static final String[] order = {"Mean" , "Median", "Variance", "Std. Deviation"}; + + public static EvAStatisticalEvaluationParams statsParams = new EvAStatisticalEvaluationParams(); + +// public static void evaluate(EvAJob[] jobList, int[] selectedIndices) { +// if (TRACE) System.out.println("Job list: " + BeanInspector.toString(jobList)); +// JTextoutputFrame textout = new JTextoutputFrame("Statistics"); +// textout.setShow(true); +// ArrayList jobsToWorkWith = new ArrayList(); +// for (int i=0; i commonFields = getCommonFields(jobsToWorkWith); +// if (commonFields!=null && !commonFields.isEmpty()) for (String field : commonFields) { +// textout.println("Checking field " + field); +// +// for (int i=0; i jobsToWorkWith = new ArrayList(); + for (int i=0; i commonFields = getCommonFields(jobsToWorkWith); + if (commonFields!=null && !commonFields.isEmpty()) for (String field : commonFields) { + textout.println("###\t"+ field + " statistical evaluation"); + + if(singleStats.length > 0){ + textout.println("one-sampled statistics"); + for (int j=-1; j 0){ + textout.println("two-sampled stats:"); + for(int i=0; i jobsToWorkWith, + String field) { + for (int i=0; i jobsToWorkWith, String field) { + for (int i=0; i jobsToWorkWith, String field) { + for (int i=0; i jobsToWorkWith, String field) { + for (int i=0; i jobsToWorkWith) { + for(int i=0; iavg2) return "1"; + else return "0"; + } + + /** + * Return a list of field names which occur in all jobs. + * @param jobList + * @return + */ + private static List getCommonFields(List jobList) { + List lSoFar=null, tmpL = new LinkedList(); + for (EvAJob j:jobList) { + if (lSoFar==null) { + lSoFar = new LinkedList(); + for (String f : j.getFieldHeaders()) lSoFar.add(f); + } else { + for (String f : lSoFar) { + if (j.getFieldIndex(f)>=0) tmpL.add(f); + } + lSoFar=tmpL; + tmpL = new LinkedList(); + } + } + if (TRACE) System.out.println("Common fields are " + BeanInspector.toString(lSoFar)); + return lSoFar; + } + +} diff --git a/src/eva2/server/stat/EvAStatisticalEvaluationParams.java b/src/eva2/server/stat/EvAStatisticalEvaluationParams.java new file mode 100644 index 00000000..fc931ef3 --- /dev/null +++ b/src/eva2/server/stat/EvAStatisticalEvaluationParams.java @@ -0,0 +1,61 @@ +package eva2.server.stat; + +import java.io.Serializable; +import java.util.List; + +import javax.swing.JButton; + +import eva2.tools.StringSelection; + +public class EvAStatisticalEvaluationParams implements Serializable { + +// private StatsOnDataSetPairEnum[] pairedStats = new StatsOnDataSetPairEnum[] {StatsOnDataSetPairEnum.tTestEqual, StatsOnDataSetPairEnum.tTestUnequal}; +// private StatsOnSingleDataSetEnum[] singleStats = new StatsOnSingleDataSetEnum[] {StatsOnSingleDataSetEnum.mean, StatsOnSingleDataSetEnum.median}; + + private StringSelection singleStats = new StringSelection(StatsOnSingleDataSetEnum.mean, StatsOnSingleDataSetEnum.getInfoStrings()); + private StringSelection twoSampledStats = new StringSelection(StatsOnTwoSampledDataEnum.tTestUneqLenEqVar, StatsOnTwoSampledDataEnum.getInfoStrings()); + private List additionalButtons = null; + + public void setGenericAdditionalButtons(List buts) { + this.additionalButtons = buts; + } + + public StringSelection getTwoSampledStats() { + return twoSampledStats; + } + public void setTwoSampledStats(StringSelection pairedStats) { + this.twoSampledStats = pairedStats; + } + public String twoSampledStatsTipText() { + return "Statistical tests on two-sampled data"; + } + + public StringSelection getOneSampledStats() { + return singleStats; + } + public void setOneSampledStats(StringSelection singleStats) { + this.singleStats = singleStats; + } + public String oneSampledStatsTipText() { + return "Statistical tests on one-sampled data"; + } + + public String getName() { + return "Statistical evaluation parameters"; + } + + public String globalInfo() { + return "Select statistical values to be calculated and paired tests to be performed."; + } + + public List getAdditionalButtons() { + return additionalButtons; + } + + public boolean withGenericOkButton() { + return false; + } + public boolean withGenericLoadSafeButtons() { + return false; + } +} diff --git a/src/eva2/server/stat/InterfaceStatisticsListener.java b/src/eva2/server/stat/InterfaceStatisticsListener.java index ad173ca0..3ad78a76 100644 --- a/src/eva2/server/stat/InterfaceStatisticsListener.java +++ b/src/eva2/server/stat/InterfaceStatisticsListener.java @@ -49,4 +49,12 @@ public interface InterfaceStatisticsListener { * @param multiRunFinalObjectData */ public void finalMultiRunResults(String[] header, List multiRunFinalObjectData); + + /** + * Called after the job is finished. Return true if the listener should be removed after this multi-run. + * + * @param header + * @param multiRunFinalObjectData + */ + public boolean notifyMultiRunFinished(String[] header, List multiRunFinalObjectData); } diff --git a/src/eva2/tools/ReflectPackage.java b/src/eva2/tools/ReflectPackage.java index 17c3cfa5..3a07b304 100644 --- a/src/eva2/tools/ReflectPackage.java +++ b/src/eva2/tools/ReflectPackage.java @@ -5,6 +5,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; +import java.lang.reflect.Constructor; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -18,6 +19,8 @@ import java.util.jar.JarInputStream; import javax.management.MBeanServer; import javax.management.ObjectName; +import eva2.gui.BeanInspector; + /** * Allow for java to list Classes that exist in one package and can be instantiated from @@ -474,4 +477,80 @@ public class ReflectPackage { } return ret; } + + /** + * Instantiate a class given by full name (with package) and try to set the member values given + * in the pair-value list. Returns null if the instance could not be created or any name-value + * pair could not be set. Otherwise the created object is returned. + * + * @param clsName + * @param paramValuePairs + * @return + */ + public static Object instantiateWithParams(String clsName, List> paramValuePairs) { + return instantiateWithParams(clsName, new Object[]{}, paramValuePairs); + } + + /** + * Instantiate a class given by full name (with package) and try to set the member values given + * in the pair-value list. Returns null if the instance could not be created or any name-value + * pair could not be set. Otherwise the created object is returned. + * + * @param clsName name of the target class with full package path + * @param args constructor arguments + * @param paramValuePairs pairs of values to set using generic setter methods + * @see BeanInspector.setMem(Object,String,Object) + * @return + */ + public static Object instantiateWithParams(String clsName, Object[] args, List> paramValuePairs) { + Object o = getInstance(clsName, args); + if (o!=null) { + if (paramValuePairs!=null) for (Pair nameVal : paramValuePairs) { + boolean succ = BeanInspector.setMem(o, nameVal.head, nameVal.tail); + if (!succ) { + System.err.println("Error, unable to set " + nameVal.head + " to " + nameVal.tail + " in object " + o); + return null; + } + else if (TRACE) System.out.println("Successfully set " + nameVal.head + " to " + nameVal.tail + " in object " + o); + } + return o; + } else { + System.err.println("Error in instantiateWithParams!"); + return null; + } + } + + /** + * Retrieve an instance of a generic object with arbitrary arguments. Note that the + * full package path must be given and the argument array must match a signature of + * an existing constructor. + * Returns null on a failure and the constructed object otherwise. + * + * @param clsName + * @param args + * @return + */ + public static Object getInstance(String clsName, Object[] args) { + Object o; + try { + Class clz = Class.forName(clsName); + Class[] argClz=new Class[args.length]; + for (int i=0; i ct; + try { + ct = clz.getConstructor(argClz); + o=ct.newInstance(args); + } catch (Exception e) { + System.err.println("Unable to retrieve constructor of " + clsName + ", arguments " + BeanInspector.toString(args)+"\n"+e.getClass()); + System.err.println(e.getMessage()); + e.printStackTrace(); + o=null; + } + } catch (Exception e) { + System.err.println("Unable to create instance of " + clsName + ", arguments " + BeanInspector.toString(args) + "\n"+e.getMessage()); + e.printStackTrace(System.err); + o=null; + } + return o; + } } \ No newline at end of file diff --git a/src/eva2/tools/math/Mathematics.java b/src/eva2/tools/math/Mathematics.java index 08a457b3..195ee617 100644 --- a/src/eva2/tools/math/Mathematics.java +++ b/src/eva2/tools/math/Mathematics.java @@ -609,6 +609,71 @@ public class Mathematics { } } + + public static double median2(double[] vector, boolean clone){ + double[] in; + if (clone){ + in = (double[]) vector.clone(); + }else{ + in = vector; + } + if(in.length == 0){ + return 0; + } + Arrays.sort(in); + return in[(int) Math.floor(((double) in.length) / 2.0)]; + } + + public static double variance(double[] vector){ + double mean = Mathematics.mean(vector); + double result = 0.0; + for(int i=0; i