/* * 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.helpers.ALDClassInfo; import de.unihalle.informatik.Alida.operator.ALDParameterDescriptor; import de.unihalle.informatik.Alida.annotations.ALDDataIOProvider; import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerSwing; import de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwing; import de.unihalle.informatik.Alida.dataio.provider.helpers.ALDParametrizedClassDataIOHelper; import de.unihalle.informatik.Alida.dataio.provider.helpers.ALDParametrizedClassDummy; import de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDParametrizedClassConfigWindow; import de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDSwingComponent; import de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDSwingComponentComboBox; import de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDSwingComponentComboBoxItem; import de.unihalle.informatik.Alida.dataio.provider.swing.events.ALDSwingValueChangeEvent; import de.unihalle.informatik.Alida.dataio.provider.swing.events.ALDSwingValueChangeListener; import de.unihalle.informatik.Alida.exceptions.ALDDataIOException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException.*; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.Set; import java.util.Vector; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; /** * Class for generic handling of parameterized classes in GUI. * <p> * This class implements the <code>ALDDataIOSwing</code> interface for * parametrized classes. It provides a button linked to a configuration * window that subsumes all parameters of the class to configure. * * @author moeller */ @ALDDataIOProvider public class ALDParametrizedClassDataIOSwing implements ALDDataIOSwing { /** * GUI configuration element. */ private ParametrizedClassPanel paramPanel; /** * Default constructor. */ public ALDParametrizedClassDataIOSwing() { // nothing to do here } /** * Interface method to announce class for which IO is provided for. * * @return Collection of classes provided. */ @Override public Collection<Class<?>> providedClasses() { LinkedList<Class<?>> classes = new LinkedList<Class<?>>(); classes.add( ALDParametrizedClassDummy.class); return classes; } @Override public Object getInitialGUIValue(Field field, Class<?> cl, Object obj, ALDParameterDescriptor descr) throws ALDDataIOProviderException { if (obj == null) return null; // iterate over all parameters and request default values HashMap<String,Field> params = ALDParametrizedClassDataIOHelper.getAnnotatedFields( obj.getClass()); Set<String> paramNames = params.keySet(); for (String pname : paramNames) { try { Field f = params.get(pname); Object currentValue = ALDParametrizedClassDataIOHelper.getValue(f, obj); Class<?> currentValueClass = f.getType(); if (currentValue != null) currentValueClass = currentValue.getClass(); Object initialValue = ALDDataIOManagerSwing.getInstance().getInitialGUIValue( f, currentValueClass, currentValue, descr); ALDParametrizedClassDataIOHelper.setValue(f, obj, initialValue); } catch (Exception e) { e.printStackTrace(); throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "[" + this.getClass().getName() + "]" + ": could not get initial GUI value for " + "parameter <" + pname + "> of class <" + cl + ">!"); } } return obj; } /* (non-Javadoc) * @see de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwing#createGUIElement(java.lang.reflect.Field, java.lang.Class, java.lang.Object) */ @Override public ParametrizedClassPanel createGUIElement( Field field, Class<?> cl, Object obj, ALDParameterDescriptor descr) throws ALDDataIOProviderException { try { this.paramPanel = new ParametrizedClassPanel(field, cl, obj, descr); } catch (ALDDataIOException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "Provider Error: " + e.getCommentString()); } return this.paramPanel; } /* (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 (!(guiElement instanceof ParametrizedClassPanel)) throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.INVALID_GUI_ELEMENT, "ParametrizedClassDataIO: setValue() received invalid GUI element!"); ParametrizedClassPanel paramGUIPanel = (ParametrizedClassPanel)guiElement; try { paramGUIPanel.setValue(field, cl, value); } catch(ALDDataIOException exp) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "setValue() - setting parameter in GUI failed!"); } } /* (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 ParametrizedClassPanel)) throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.INVALID_GUI_ELEMENT, "ParametrizedClassDataIO: " + "readData received invalid GUI element!"); ParametrizedClassPanel paramGUIPanel = (ParametrizedClassPanel)guiElement; try { Object obj = paramGUIPanel.readData(field, cl); return obj; } catch (ALDDataIOException e) { // if exception came from provider, just throw it to parent if (e instanceof ALDDataIOProviderException) throw (ALDDataIOProviderException)e; // otherwise embed it into our own exception // (... necessary due to method signature) throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "Data IO Manager returned an error: \n" + e.getCommentString()); } } /* (non-Javadoc) * @see de.unihalle.informatik.Alida.dataio.provider.ALDDataIOSwing#writeData(java.lang.Object) */ @Override public JComponent writeData(Object obj, ALDParameterDescriptor descr) { return new ParametrizedClassShowButton(obj, descr); } /** * GUI element class for parametrized class parameters. * * @author moeller */ private class ParametrizedClassPanel extends ALDSwingComponent implements ActionListener, ItemListener, ALDSwingValueChangeListener { /** * Default item if no value is selected. */ private ALDSwingComponentComboBoxItem boxItemNone = new ALDSwingComponentComboBoxItem(null, "none", "none"); /** * Main panel containing elements for class selection. */ private JPanel mainPanel; /** * Combobox containing available classes to select. */ private ALDSwingComponentComboBox classSelection; /** * List of available classes. */ @SuppressWarnings("rawtypes") private Collection<Class> availableClasses; /** * Hashmap to remember short class names. */ @SuppressWarnings("rawtypes") private HashMap<String, Class> shortNames = new HashMap<String, Class>(); /** * Hashmap to store configuration windows for classes. */ @SuppressWarnings("rawtypes") private HashMap<Class, ALDParametrizedClassConfigWindow> configWins = new HashMap<Class, ALDParametrizedClassConfigWindow>(); /** * Default constructor. * @param field Field linked to the parametrized class parameter. * @param cl Class of the parametrized class parameter. * @param obj Initial object to use for initialization. * @param descr Descriptor linked to the parameter. * @throws ALDDataIOException Thrown in case of failure. */ public ParametrizedClassPanel( @SuppressWarnings("unused") Field field, Class<?> cl, Object obj, ALDParameterDescriptor descr) throws ALDDataIOException { this.mainPanel = new JPanel(); this.mainPanel.setLayout(new FlowLayout()); // combo box for possible classes this.availableClasses = ALDClassInfo.lookupExtendingClasses(cl); // add the class itself if it is not abstract nor an interface if ( !(cl.isInterface()) && !(Modifier.isAbstract(cl.getModifiers())) && !(this.availableClasses.contains(cl)) ) this.availableClasses.add(cl); Vector<ALDSwingComponentComboBoxItem> comboFields = new Vector<ALDSwingComponentComboBoxItem>(); for (Class<?> c : this.availableClasses) { String shortName = c.getSimpleName(); comboFields.add( new ALDSwingComponentComboBoxItem(c, shortName, c.getCanonicalName())); this.shortNames.put(shortName, c); // generate configuration window for each class ALDParametrizedClassConfigWindow win = new ALDParametrizedClassConfigWindow(c, descr); win.addValueChangeEventListener(this); this.configWins.put(c, win); } // sort list of classes lexicographically Collections.sort(comboFields); // add dummy to reset selection at beginning of list comboFields.add(0, this.boxItemNone); // init combobox this.classSelection = new ALDSwingComponentComboBox(descr, comboFields); this.classSelection.addValueChangeEventListener(this); // set the default value... if ( obj != null ) { // check which name convention to use this.classSelection.setSelectedItem(obj); ALDParametrizedClassConfigWindow win = this.configWins.get(obj.getClass()); try { win.setValue(obj); } catch (NullPointerException e2) { System.err.println("Attention! Default object has wrong type, " + " class " + obj.getClass() + " not found!!!"); } } else { this.classSelection.setSelectedItem(this.boxItemNone); } // configuration button JButton confButton = new JButton("Configure..."); confButton.setActionCommand("configure"); confButton.addActionListener(this); // reset button JButton resetButton = new JButton("Reset"); resetButton.setActionCommand("reset"); resetButton.addActionListener(this); this.mainPanel.add(this.classSelection.getJComponent()); this.mainPanel.add(confButton); this.mainPanel.add(resetButton); } /* (non-Javadoc) * @see de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDSwingComponent#getJComponent() */ @Override public JPanel getJComponent() { return this.mainPanel; } /** * Function to update values of parametrized class object. * @param field Field linked to the parameter to update. * @param cl Class of the parameter to update. * @param value New value to set. * @throws ALDDataIOException Thrown in case of update failure. */ @SuppressWarnings("unused") public void setValue(Field field, Class<?> cl, Object value) throws ALDDataIOException { if (value == null) return; Class<?> objClass = value.getClass(); this.classSelection.setSelectedItem(value); ALDParametrizedClassConfigWindow win = this.configWins.get(objClass); if (win != null) { win.setValue(value); } else { System.err.println("ALDParametrizedClassDataIOSwing: \n" + "Configuration window for requested class doesn't exist..."); } } /** * Function to read parameter values from GUI. * * @param field Field of object. * @param cl Class of object. * @return Current object value. * @throws ALDDataIOException Thrown in case of failure. */ public Object readData(Field field, @SuppressWarnings("unused") Class<?> cl) throws ALDDataIOException { // get selected item Object item = this.classSelection.getJComponent().getSelectedItem(); // Class<?> selectedClass = null; // if (item instanceof String) // selectedClass = this.shortNames.get(item); // else // selectedClass = (Class<?>)item; Class<?> selectedClass = (Class<?>)((ALDSwingComponentComboBoxItem)item).getObject(); if (this.configWins.get(selectedClass) == null) { return null; } return this.configWins.get(selectedClass).readData(field, selectedClass); } /* (non-Javadoc) * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); // pop-up the parametrized class configuration window if (command.equals("configure")) { // get selected item Object item = this.classSelection.getJComponent().getSelectedItem(); if (item.equals(this.boxItemNone)) return; Class<?> selectedClass = (Class<?>)((ALDSwingComponentComboBoxItem)item).getObject(); try { // trigger event this.configWins.get(selectedClass).setVisible(true); this.handleValueChangeEvent( new ALDSwingValueChangeEvent(this, null)); } catch (ALDDataIOProviderException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } else if (command.equals("reset")) { // close all windows @SuppressWarnings("rawtypes") Set<Class> keys = this.configWins.keySet(); for (Class<?> k: keys) { try { this.configWins.get(k).setVisible(false); } catch (ALDDataIOProviderException e1) { // we can savely ignore this exception... } } // reset selection to 'none' this.classSelection.getJComponent().setSelectedItem( this.boxItemNone); // trigger event this.handleValueChangeEvent( new ALDSwingValueChangeEvent(this, null)); } } /* (non-Javadoc) * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) */ @Override @SuppressWarnings("unchecked") public void itemStateChanged(ItemEvent e) { this.classSelection.getJComponent().removeAllItems(); // generate list of available classes for sorting // Vector<String> comboFields = new Vector<String>(); // for (Class<?> c : this.availableClasses) { // comboFields.add(c.getSimpleName()); // } LinkedList<ALDSwingComponentComboBoxItem> comboFields = new LinkedList<ALDSwingComponentComboBoxItem>(); for (Class<?> c : this.availableClasses) { String shortName = c.getSimpleName(); // add item object to combobox fields comboFields.add(new ALDSwingComponentComboBoxItem(c, shortName, c.getCanonicalName())); } // sort list of classes lexicographically Collections.sort(comboFields); comboFields.add(0, this.boxItemNone); // check which name convention to use and fill box // if (this.longNameCheckBox.isSelected()) { // for (String s: comboFields) { // for (@SuppressWarnings("rawtypes") Class c: this.availableClasses) { // if (c.getSimpleName().equals(s)) { // this.classSelection.getJComponent().addItem(c); // break; // } // } // } // } // else { // for (String s: comboFields) { // this.classSelection.getJComponent().addItem(s); // } // } // // check which name convention to use and fill box // if (this.longNameCheckBox.isSelected()) { // for (@SuppressWarnings("rawtypes") Class c: this.availableClasses) { // this.classSelection.addItem(c); // } // } // else { // for (@SuppressWarnings("rawtypes") Class c: this.availableClasses) { // this.classSelection.addItem(c.getSimpleName()); // } // } for (ALDSwingComponentComboBoxItem item: comboFields) { this.classSelection.getJComponent().addItem(item); } } /* (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() { this.classSelection.disableComponent(); @SuppressWarnings("rawtypes") Set<Class> keys = this.configWins.keySet(); for (@SuppressWarnings("rawtypes") Class key: keys) { ALDParametrizedClassConfigWindow win = this.configWins.get(key); if (win != null) win.disableComponent(); } } @Override public void enableComponent() { this.classSelection.enableComponent(); @SuppressWarnings("rawtypes") Set<Class> keys = this.configWins.keySet(); for (@SuppressWarnings("rawtypes") Class key: keys) { ALDParametrizedClassConfigWindow win = this.configWins.get(key); if (win != null) win.enableComponent(); } } @Override public void dispose() { this.classSelection.dispose(); @SuppressWarnings("rawtypes") Set<Class> keys = this.configWins.keySet(); for (@SuppressWarnings("rawtypes") Class key: keys) { ALDParametrizedClassConfigWindow win = this.configWins.get(key); if (win != null) win.dispose(); } } } /** * Button to visualize parametrized class objects. * * @author moeller */ private class ParametrizedClassShowButton extends JButton implements ActionListener { /** * Associated parameter window. */ private ALDParametrizedClassConfigWindow win; /** * Default constructor. * @param obj Object to visualize. * @param descr Descriptor associated with corresponding parameter. */ public ParametrizedClassShowButton(Object obj, ALDParameterDescriptor descr) { super("Show data..."); this.setActionCommand("show"); this.addActionListener(this); this.win = new ALDParametrizedClassConfigWindow(obj, descr, true); } @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); if (command.equals("show")) { try { this.win.setVisible(true); } catch (ALDDataIOProviderException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } } }