All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.gui.GenericArrayEditor Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This version represents the developer version, the "bleeding edge" of development, you could say. New functionality gets added to this version.

There is a newer version: 3.9.6
Show newest version
/*
 *   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 .
 */

/*
 *    GenericArrayEditor.java
 *    Copyright (C) 1999-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui;

import weka.core.SerializedObject;

import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Array;

/**
 * A PropertyEditor for arrays of objects that themselves have property editors.
 * 
 * @author Len Trigg ([email protected])
 * @version $Revision: 14496 $
 */
public class GenericArrayEditor implements PropertyEditor {

  private final CustomEditor m_customEditor;

  /**
   * This class presents a GUI for editing the array elements
   */
  private class CustomEditor extends JPanel {
    /** for serialization. */
    private static final long serialVersionUID = 3914616975334750480L;

    /** Handles property change notification. */
    private final PropertyChangeSupport m_Support = new PropertyChangeSupport(
      GenericArrayEditor.this);

    /** The label for when we can't edit that type. */
    private final JLabel m_Label = new JLabel("Can't edit",
      SwingConstants.CENTER);

    /** The list component displaying current values. */
    private final JList m_ElementList = new JList();

    /** The class of objects allowed in the array. */
    private Class m_ElementClass = String.class;

    /** The defaultlistmodel holding our data. */
    private DefaultListModel m_ListModel;

    /** The property editor for the class we are editing. */
    private PropertyEditor m_ElementEditor;

    /** Click this to delete the selected array values. */
    private final JButton m_DeleteBut = new JButton("Delete");

    /** Click this to edit the selected array value. */
    private final JButton m_EditBut = new JButton("Edit");

    /** Click this to move the selected array value(s) one up. */
    private final JButton m_UpBut = new JButton("Up");

    /** Click this to move the selected array value(s) one down. */
    private final JButton m_DownBut = new JButton("Down");

    /** Click to add the current object configuration to the array. */
    private final JButton m_AddBut = new JButton("Add");

    /** The property editor for editing existing elements. */
    private PropertyEditor m_Editor = new GenericObjectEditor();

    /** The currently displayed property dialog, if any. */
    private PropertyDialog m_PD;

    /** Listens to buttons being pressed and taking the appropriate action. */
    private final ActionListener m_InnerActionListener = new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {

        if (e.getSource() == m_DeleteBut) {
          int[] selected = m_ElementList.getSelectedIndices();
          if (selected != null) {
            for (int i = selected.length - 1; i >= 0; i--) {
              int current = selected[i];
              m_ListModel.removeElementAt(current);
              if (m_ListModel.size() > current) {
                m_ElementList.setSelectedIndex(current);
              }
            }
            m_Support.firePropertyChange("", null, null);
          }
        } else if (e.getSource() == m_EditBut) {
          if (m_Editor instanceof GenericObjectEditor) {
            ((GenericObjectEditor) m_Editor).setClassType(m_ElementClass);
          }
          try {
            m_Editor.setValue(GenericObjectEditor.makeCopy(m_ElementList
              .getSelectedValue()));
          } catch (Exception ex) {
            // not possible to serialize?
            m_Editor.setValue(m_ElementList.getSelectedValue());
          }
          if (m_Editor.getValue() != null) {
            if (PropertyDialog.getParentDialog(CustomEditor.this) != null) {
              m_PD = new PropertyDialog(
                PropertyDialog.getParentDialog(CustomEditor.this), m_Editor, -1, -1);
            } else {
              m_PD = new PropertyDialog(
                PropertyDialog.getParentFrame(CustomEditor.this), m_Editor, -1, -1);
            }
            m_PD.setVisible(true);
            if (!(m_Editor instanceof GenericObjectEditor)
                    || (!((GenericObjectEditor)m_Editor).wasCancelPressed())) {
              m_ListModel.set(m_ElementList.getSelectedIndex(),
                      m_Editor.getValue());
              m_Support.firePropertyChange("", null, null);
            }
          }
        } else if (e.getSource() == m_UpBut) {
          JListHelper.moveUp(m_ElementList);
          m_Support.firePropertyChange("", null, null);
        } else if (e.getSource() == m_DownBut) {
          JListHelper.moveDown(m_ElementList);
          m_Support.firePropertyChange("", null, null);
        } else if (e.getSource() == m_AddBut) {
          int selected = m_ElementList.getSelectedIndex();
          Object addObj = m_ElementEditor.getValue();

          // Make a full copy of the object using serialization
          try {
            SerializedObject so = new SerializedObject(addObj);
            addObj = so.getObject();
            if (selected != -1) {
              m_ListModel.insertElementAt(addObj, selected);
            } else {
              m_ListModel.addElement(addObj);
            }
            m_Support.firePropertyChange("", null, null);
          } catch (Exception ex) {
            JOptionPane.showMessageDialog(CustomEditor.this,
              "Could not create an object copy", null,
              JOptionPane.ERROR_MESSAGE);
          }
        }
      }
    };

    /** Listens to list items being selected and takes appropriate action. */
    private final ListSelectionListener m_InnerSelectionListener = new ListSelectionListener() {

      @Override
      public void valueChanged(ListSelectionEvent e) {

        if (e.getSource() == m_ElementList) {
          // Enable the delete/edit button
          if (m_ElementList.getSelectedIndex() != -1) {
            m_DeleteBut.setEnabled(true);
            m_EditBut
              .setEnabled(m_ElementList.getSelectedIndices().length == 1);
            m_UpBut.setEnabled(JListHelper.canMoveUp(m_ElementList));
            m_DownBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
          }
          // disable delete/edit button
          else {
            m_DeleteBut.setEnabled(false);
            m_EditBut.setEnabled(false);
            m_UpBut.setEnabled(false);
            m_DownBut.setEnabled(false);
          }
        }
      }
    };

    /** Listens to mouse events and takes appropriate action. */
    private final MouseListener m_InnerMouseListener = new MouseAdapter() {

      @Override
      public void mouseClicked(MouseEvent e) {
        if (e.getSource() == m_ElementList) {
          if (e.getClickCount() == 2) {
            // unfortunately, locationToIndex only returns the nearest entry
            // and not the exact one, i.e. if there's one item in the list and
            // one doublelclicks somewhere in the list, this index will be
            // returned
            int index = m_ElementList.locationToIndex(e.getPoint());
            if (index > -1) {
              m_InnerActionListener.actionPerformed(new ActionEvent(m_EditBut,
                0, ""));
            }
          }
        }
      }
    };

    /**
     * Sets up the array editor.
     */
    public CustomEditor() {

      setLayout(new BorderLayout());
      add(m_Label, BorderLayout.CENTER);
      m_DeleteBut.addActionListener(m_InnerActionListener);
      m_EditBut.addActionListener(m_InnerActionListener);
      m_UpBut.addActionListener(m_InnerActionListener);
      m_DownBut.addActionListener(m_InnerActionListener);
      m_AddBut.addActionListener(m_InnerActionListener);
      m_ElementList.addListSelectionListener(m_InnerSelectionListener);
      m_ElementList.addMouseListener(m_InnerMouseListener);
      m_AddBut.setToolTipText("Add the current item to the list");
      m_DeleteBut.setToolTipText("Delete the selected list item");
      m_EditBut.setToolTipText("Edit the selected list item");
      m_UpBut.setToolTipText("Move the selected item(s) one up");
      m_DownBut.setToolTipText("Move the selected item(s) one down");
    }

    /**
     * This class handles the creation of list cell renderers from the property
     * editors.
     */
    private class EditorListCellRenderer implements ListCellRenderer {

      /** The class of the property editor for array objects. */
      private final Class m_EditorClass;

      /** The class of the array values. */
      private final Class m_ValueClass;

      /**
       * Creates the list cell renderer.
       * 
       * @param editorClass The class of the property editor for array objects
       * @param valueClass The class of the array values
       */
      public EditorListCellRenderer(Class editorClass, Class valueClass) {
        m_EditorClass = editorClass;
        m_ValueClass = valueClass;
      }

      /**
       * Creates a cell rendering component.
       * 
       * @param list the list that will be rendered in
       * @param value the cell value
       * @param index which element of the list to render
       * @param isSelected true if the cell is selected
       * @param cellHasFocus true if the cell has the focus
       * @return the rendering component
       */
      @Override
      public Component getListCellRendererComponent(final JList list,
        final Object value, final int index, final boolean isSelected,
        final boolean cellHasFocus) {
        try {
          final PropertyEditor e = (PropertyEditor) m_EditorClass.newInstance();
          if (e instanceof GenericObjectEditor) {
            // ((GenericObjectEditor) e).setDisplayOnly(true);
            ((GenericObjectEditor) e).setClassType(m_ValueClass);
          }
          e.setValue(value);
          return new JPanel() {

            private static final long serialVersionUID = -3124434678426673334L;

            @Override
            public void paintComponent(Graphics g) {

              Insets i = this.getInsets();
              Rectangle box = new Rectangle(i.left, i.top, this.getWidth()
                - i.right, this.getHeight() - i.bottom);
              g.setColor(isSelected ? list.getSelectionBackground() : list
                .getBackground());
              g.fillRect(0, 0, this.getWidth(), this.getHeight());
              g.setColor(isSelected ? list.getSelectionForeground() : list
                .getForeground());
              e.paintValue(g, box);
            }

            @Override
            public Dimension getPreferredSize() {

              Font f = this.getFont();
              FontMetrics fm = this.getFontMetrics(f);
              return new Dimension(0, fm.getHeight());
            }
          };
        } catch (Exception ex) {
          return null;
        }
      }
    }

    /**
     * Updates the type of object being edited, so attempts to find an
     * appropriate propertyeditor.
     * 
     * @param o a value of type 'Object'
     */
    private void updateEditorType(Object o) {

      // Determine if the current object is an array
      if ((o != null) && (o.getClass().isArray())) {
        Class elementClass = o.getClass().getComponentType();
        PropertyEditor editor = PropertyEditorManager.findEditor(elementClass);
        Component view = null;
        ListCellRenderer lcr = new DefaultListCellRenderer();
        if (editor != null) {
          if (editor instanceof GenericObjectEditor) {
            ((GenericObjectEditor) editor).setClassType(elementClass);
          }

          // setting the value in the editor so that
          // we don't get a NullPointerException
          // when we do getAsText() in the constructor of
          // PropertyValueSelector()
          if (Array.getLength(o) > 0) {
            editor.setValue(makeCopy(Array.get(o, 0)));
          } else {
            if (editor instanceof GenericObjectEditor) {
              ((GenericObjectEditor) editor).setDefaultValue();
            } else {
              try {
                if (editor instanceof FileEditor) {
                  editor.setValue(new java.io.File("-NONE-"));
                } else {
                  editor.setValue(elementClass.newInstance());
                }
              } catch (Exception ex) {
                m_ElementEditor = null;
                m_ListModel = null;
                removeAll();
                System.err.println(ex.getMessage());
                add(m_Label, BorderLayout.CENTER);
                m_Support.firePropertyChange("", null, null);
                validate();
                return;
              }
            }
          }

          if (editor.isPaintable() && editor.supportsCustomEditor()) {
            view = new PropertyPanel(editor);
            lcr = new EditorListCellRenderer(editor.getClass(), elementClass);
          } else if (editor.getTags() != null) {
            view = new PropertyValueSelector(editor);
          } else if (editor.getAsText() != null) {
            view = new PropertyText(editor);
          }
        }
        if (view == null) {
          JOptionPane.showMessageDialog(this,
                  "No property editor for class: "  + elementClass.getName(), "Error...",
                  JOptionPane.ERROR_MESSAGE);
          return;
        } else {
          removeAll();
          m_ElementEditor = editor;
          try {
            m_Editor = editor.getClass().newInstance();
          } catch (InstantiationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
          } catch (IllegalAccessException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
          }

          // Create the ListModel and populate it
          m_ListModel = new DefaultListModel();
          m_ElementClass = elementClass;
          for (int i = 0; i < Array.getLength(o); i++) {
            m_ListModel.addElement(Array.get(o, i));
          }
          m_ElementList.setCellRenderer(lcr);
          m_ElementList.setModel(m_ListModel);
          if (m_ListModel.getSize() > 0) {
            m_ElementList.setSelectedIndex(0);
          } else {
            m_DeleteBut.setEnabled(false);
            m_EditBut.setEnabled(false);
          }
          m_UpBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
          m_DownBut.setEnabled(JListHelper.canMoveDown(m_ElementList));

          // have already set the value above in the editor
          // try {
          // if (m_ListModel.getSize() > 0) {
          // m_ElementEditor.setValue(m_ListModel.getElementAt(0));
          // } else {
          // if (m_ElementEditor instanceof GenericObjectEditor) {
          // ((GenericObjectEditor)m_ElementEditor).setDefaultValue();
          // } else {
          // m_ElementEditor.setValue(m_ElementClass.newInstance());
          // }
          // }

          JPanel panel = new JPanel();
          panel.setLayout(new BorderLayout());
          panel.add(view, BorderLayout.CENTER);
          panel.add(m_AddBut, BorderLayout.EAST);
          add(panel, BorderLayout.NORTH);
          add(new JScrollPane(m_ElementList), BorderLayout.CENTER);
          JPanel panel2 = new JPanel();
          panel2.setLayout(new GridLayout(1, 4));
          panel2.add(m_DeleteBut);
          panel2.add(m_EditBut);
          panel2.add(m_UpBut);
          panel2.add(m_DownBut);
          add(panel2, BorderLayout.SOUTH);
          m_ElementEditor
            .addPropertyChangeListener(new PropertyChangeListener() {
              @Override
              public void propertyChange(PropertyChangeEvent e) {
                repaint();
              }
            });
          // } catch (Exception ex) {
          // System.err.println(ex.getMessage());
          // m_ElementEditor = null;
          // }
        }
      }
      if (m_ElementEditor == null) {
        add(m_Label, BorderLayout.CENTER);
      }
      m_Support.firePropertyChange("", null, null);
      validate();
    }
  }

  public GenericArrayEditor() {
    m_customEditor = new CustomEditor();
  }

  /**
   * Sets the current object array.
   * 
   * @param o an object that must be an array.
   */
  @Override
  public void setValue(Object o) {

    // Create a new list model, put it in the list and resize?
    m_customEditor.updateEditorType(o);
  }

  /**
   * Gets the current object array.
   * 
   * @return the current object array
   */
  @Override
  public Object getValue() {

    if (m_customEditor.m_ListModel == null) {
      return null;
    }
    // Convert the listmodel to an array of strings and return it.
    int length = m_customEditor.m_ListModel.getSize();
    Object result = Array.newInstance(m_customEditor.m_ElementClass, length);
    for (int i = 0; i < length; i++) {
      Array.set(result, i, m_customEditor.m_ListModel.elementAt(i));
    }
    return result;
  }

  /**
   * Supposedly returns an initialization string to create a classifier
   * identical to the current one, including it's state, but this doesn't appear
   * possible given that the initialization string isn't supposed to contain
   * multiple statements.
   * 
   * @return the java source code initialisation string
   */
  @Override
  public String getJavaInitializationString() {

    return "null";
  }

  /**
   * Returns true to indicate that we can paint a representation of the string
   * array.
   * 
   * @return true
   */
  @Override
  public boolean isPaintable() {
    return true;
  }

  /**
   * Paints a representation of the current classifier.
   * 
   * @param gfx the graphics context to use
   * @param box the area we are allowed to paint into
   */
  @Override
  public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {

    FontMetrics fm = gfx.getFontMetrics();
    int vpad = (box.height - fm.getHeight()) / 2;
    String rep = m_customEditor.m_ListModel.getSize() + " "
      + m_customEditor.m_ElementClass.getName();
    gfx.drawString(rep, 2, fm.getAscent() + vpad + 2);
  }

  /**
   * Returns null as we don't support getting/setting values as text.
   * 
   * @return null
   */
  @Override
  public String getAsText() {
    return null;
  }

  /**
   * Returns null as we don't support getting/setting values as text.
   * 
   * @param text the text value
   * @exception IllegalArgumentException as we don't support getting/setting
   *              values as text.
   */
  @Override
  public void setAsText(String text) {
    throw new IllegalArgumentException(text);
  }

  /**
   * Returns null as we don't support getting values as tags.
   * 
   * @return null
   */
  @Override
  public String[] getTags() {
    return null;
  }

  /**
   * Returns true because we do support a custom editor.
   * 
   * @return true
   */
  @Override
  public boolean supportsCustomEditor() {
    return true;
  }

  /**
   * Returns the array editing component.
   * 
   * @return a value of type 'java.awt.Component'
   */
  @Override
  public java.awt.Component getCustomEditor() {
    return m_customEditor;
  }

  /**
   * Adds a PropertyChangeListener who will be notified of value changes.
   * 
   * @param l a value of type 'PropertyChangeListener'
   */
  @Override
  public void addPropertyChangeListener(PropertyChangeListener l) {
    m_customEditor.m_Support.addPropertyChangeListener(l);
  }

  /**
   * Removes a PropertyChangeListener.
   * 
   * @param l a value of type 'PropertyChangeListener'
   */
  @Override
  public void removePropertyChangeListener(PropertyChangeListener l) {
    m_customEditor.m_Support.removePropertyChangeListener(l);
  }

  /**
   * Makes a copy of an object using serialization.
   * 
   * @param source the object to copy
   * @return a copy of the source object, null if copying fails
   */
  public static Object makeCopy(Object source) {
    Object result;

    try {
      result = GenericObjectEditor.makeCopy(source);
    } catch (Exception e) {
      result = null;
    }

    return result;
  }

  /**
   * Tests out the array editor from the command line.
   * 
   * @param args ignored
   */
  public static void main(String[] args) {

    try {
      GenericObjectEditor.registerEditors();

      final GenericArrayEditor ce = new GenericArrayEditor();

      final weka.filters.Filter[] initial = new weka.filters.Filter[0];
      /*
       * { new weka.filters.AddFilter() };
       */
      /*
       * final String [] initial = { "Hello", "There", "Bob" };
       */
      PropertyDialog pd = new PropertyDialog((Frame) null, ce, 100, 100);
      pd.setSize(200, 200);
      pd.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
      ce.setValue(initial);
      pd.setVisible(true);
      // ce.validate();
    } catch (Exception ex) {
      ex.printStackTrace();
      System.err.println(ex.getMessage());
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy