From ef6033c30379e85756d6cbf2adb1cae4d58d7d7b Mon Sep 17 00:00:00 2001 From: Fabian Becker Date: Thu, 17 Dec 2015 15:52:41 +0100 Subject: [PATCH] Finish implementation of ObjectArrayEditor --- .../java/eva2/gui/PropertyEditorProvider.java | 36 ++- .../java/eva2/gui/PropertySheetPanel.java | 26 +-- .../java/eva2/gui/editor/ArrayEditor.java | 44 ++-- .../eva2/gui/editor/ObjectArrayEditor.java | 213 ++++++++++++++++++ src/main/java/eva2/tools/Primitives.java | 33 +++ 5 files changed, 295 insertions(+), 57 deletions(-) create mode 100644 src/main/java/eva2/gui/editor/ObjectArrayEditor.java create mode 100644 src/main/java/eva2/tools/Primitives.java diff --git a/src/main/java/eva2/gui/PropertyEditorProvider.java b/src/main/java/eva2/gui/PropertyEditorProvider.java index 212530f3..34166a51 100644 --- a/src/main/java/eva2/gui/PropertyEditorProvider.java +++ b/src/main/java/eva2/gui/PropertyEditorProvider.java @@ -3,12 +3,17 @@ package eva2.gui; import eva2.gui.editor.*; import eva2.optimization.individuals.codings.gp.GPArea; import eva2.optimization.operator.terminators.InterfaceTerminator; +import eva2.tools.Primitives; import eva2.tools.SelectedTag; import eva2.tools.StringSelection; import java.beans.PropertyDescriptor; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; public class PropertyEditorProvider { // if true, we use the GenericObjectEditor whenever no specific one is registered, so keep it true @@ -23,13 +28,24 @@ public class PropertyEditorProvider { public static PropertyEditor findEditor(Class cls) { PropertyEditor editor = PropertyEditorManager.findEditor(cls); + // Try to unwrap primitives + if (editor == null && Primitives.isWrapperType(cls)) { + editor = PropertyEditorManager.findEditor(Primitives.unwrap(cls)); + } + if ((editor == null) && useDefaultGOE) { if (cls.isArray()) { - editor = new ArrayEditor(); + Class unwrapped = Primitives.isWrapperType(cls.getComponentType()) ? Primitives.unwrap(cls.getComponentType()) : cls; + if (unwrapped.isPrimitive()) { + editor = new ArrayEditor(); + } else { + editor = new ObjectArrayEditor<>(unwrapped.getComponentType()); + } } else if (cls.isEnum()) { editor = new EnumEditor(); } else { editor = new GenericObjectEditor(); + ((GenericObjectEditor)editor).setClassType(cls); } } return editor; @@ -42,6 +58,7 @@ public class PropertyEditorProvider { * @return */ public static PropertyEditor findEditor(PropertyDescriptor prop, Object value) { + PropertyEditor editor = null; Class pec = prop.getPropertyEditorClass(); Class type = prop.getPropertyType(); @@ -56,10 +73,9 @@ public class PropertyEditorProvider { if (editor == null) { if (value != null) { - - // ToDo: This should be handled by the registerEditor below. findEditor however always returns the sun.beans.editor stuff. - if (value instanceof Enum) { - editor = new EnumEditor(); + // Try to unwrap primitives + if (Primitives.isWrapperType(value.getClass())) { + editor = PropertyEditorManager.findEditor(Primitives.unwrap(value.getClass())); } else { editor = PropertyEditorManager.findEditor(value.getClass()); } @@ -84,11 +100,17 @@ public class PropertyEditorProvider { if ((editor == null) && useDefaultGOE) { if (type.isArray()) { - editor = new ArrayEditor(); + Class unwrapped = Primitives.isWrapperType(type.getComponentType()) ? Primitives.unwrap(type.getComponentType()) : type; + if (unwrapped.isPrimitive()) { + editor = new ArrayEditor(); + } else { + editor = new ObjectArrayEditor<>(unwrapped.getComponentType()); + } } else if (type.isEnum()) { editor = new EnumEditor(); } else { editor = new GenericObjectEditor(); + ((GenericObjectEditor)editor).setClassType(type); } } } @@ -116,6 +138,8 @@ public class PropertyEditorProvider { PropertyEditorManager.registerEditor(Enum.class, EnumEditor.class); PropertyEditorManager.registerEditor(int[].class, ArrayEditor.class); PropertyEditorManager.registerEditor(double[].class, ArrayEditor.class); + PropertyEditorManager.registerEditor(String[].class, ArrayEditor.class); + PropertyEditorManager.registerEditor(InterfaceTerminator[].class, ArrayEditor.class); diff --git a/src/main/java/eva2/gui/PropertySheetPanel.java b/src/main/java/eva2/gui/PropertySheetPanel.java index f1617e25..578696d0 100644 --- a/src/main/java/eva2/gui/PropertySheetPanel.java +++ b/src/main/java/eva2/gui/PropertySheetPanel.java @@ -227,7 +227,7 @@ public final class PropertySheetPanel extends JPanel implements PropertyChangeLi gbConstraints.fill = GridBagConstraints.HORIZONTAL; gbConstraints.anchor = GridBagConstraints.PAGE_START; - add(buildTitledSeperator("Info"), gbConstraints); + add(new TitledSeparator("Info"), gbConstraints); gbConstraints.gridy = 1; add(infoPanel, gbConstraints); @@ -307,9 +307,7 @@ public final class PropertySheetPanel extends JPanel implements PropertyChangeLi gbConstraints.gridy = 2; - - - add(buildTitledSeperator("Properties"), gbConstraints); + add(new TitledSeparator("Properties"), gbConstraints); JScrollPane scrollableTable = new JScrollPane(propertyTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); gbConstraints.gridx = 0; @@ -320,6 +318,8 @@ public final class PropertySheetPanel extends JPanel implements PropertyChangeLi scrollableTable.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); add(scrollableTable, gbConstraints); + setMinimumSize(new Dimension(350, 0)); + validate(); setVisible(true); } @@ -333,24 +333,6 @@ public final class PropertySheetPanel extends JPanel implements PropertyChangeLi return label; } - private static JPanel buildTitledSeperator(String title) { - JPanel titledSeperator = new JPanel(new GridBagLayout()); - - GridBagConstraints gbConstraints = new GridBagConstraints(); - gbConstraints.gridx = 0; - gbConstraints.gridy = 0; - - titledSeperator.add(new JLabel("" + title), gbConstraints); - - gbConstraints.gridx = 1; - gbConstraints.gridy = 0; - gbConstraints.weightx = 1.0; - gbConstraints.fill = GridBagConstraints.HORIZONTAL; - titledSeperator.add(new JSeparator(JSeparator.HORIZONTAL), gbConstraints); - - return titledSeperator; - } - public static PropertyDescriptor[] getProperties(Object target) { BeanInfo bi; try { diff --git a/src/main/java/eva2/gui/editor/ArrayEditor.java b/src/main/java/eva2/gui/editor/ArrayEditor.java index 645b3c28..b316c69e 100644 --- a/src/main/java/eva2/gui/editor/ArrayEditor.java +++ b/src/main/java/eva2/gui/editor/ArrayEditor.java @@ -79,7 +79,7 @@ public class ArrayEditor extends JPanel implements PropertyEditor { @Override public void actionPerformed(ActionEvent e) { - boolean consistentView = true; // be optimistic... + boolean consistentView; if (view instanceof PropertyText) { // check consistency! consistentView = ((PropertyText) view).checkConsistency(); if (!consistentView) { @@ -238,7 +238,7 @@ public class ArrayEditor extends JPanel implements PropertyEditor { list.ensureIndexIsVisible(index); propPanel.getEditor().setValue(item); propPanel.showDialog(); - propPanel = null; + } } } @@ -401,7 +401,6 @@ public class ArrayEditor extends JPanel implements PropertyEditor { } } - //setPreferredSize(new Dimension(400,500)); if (withAddButton && !(upperButtonList.contains(addButton))) { upperButtonList.add(addButton); @@ -415,9 +414,6 @@ public class ArrayEditor extends JPanel implements PropertyEditor { // Upper Button Panel JPanel combiUpperPanel = new JPanel(getButtonLayout(0, upperButtonList)); - // ToDo Figure out how to now show this on Job Pane - combiUpperPanel.add(view); - view.setVisible(withAddButton); for (JButton but : upperButtonList) { combiUpperPanel.add(but); @@ -431,6 +427,12 @@ public class ArrayEditor extends JPanel implements PropertyEditor { gbConstraints.gridy = 0; add(combiUpperPanel, gbConstraints); + gbConstraints.gridy++; + gbConstraints.fill = GridBagConstraints.HORIZONTAL; + gbConstraints.weightx = 1.0; + add(view, gbConstraints); + view.setVisible(withAddButton); + // Job List gbConstraints.gridy++; gbConstraints.fill = GridBagConstraints.BOTH; @@ -461,13 +463,7 @@ public class ArrayEditor extends JPanel implements PropertyEditor { add(additionalCenterComp, gbConstraints); } - elementEditor.addPropertyChangeListener(new PropertyChangeListener() { - - @Override - public void propertyChange(final PropertyChangeEvent event) { - repaint(); - } - }); + elementEditor.addPropertyChangeListener(event -> repaint()); addPopupMenu(); } catch (Exception ex) { @@ -493,8 +489,8 @@ public class ArrayEditor extends JPanel implements PropertyEditor { * @return */ private LayoutManager getButtonLayout(int additionalOffset, List bList) { - int lines = 1 + ((bList.size() + additionalOffset - 1) / 3); int cols = 3; + int lines = 1 + ((bList.size() + additionalOffset - 1) / cols); return new GridLayout(lines, cols); } @@ -531,15 +527,11 @@ public class ArrayEditor extends JPanel implements PropertyEditor { * @return */ private ActionListener makeSelectionKnownAL(final ActionListener al) { - return new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - if (selectableList != null) { - selectableList.setSelectionByIndices(elementList.getSelectedIndices()); - } - al.actionPerformed(e); + return e -> { + if (selectableList != null) { + selectableList.setSelectionByIndices(elementList.getSelectedIndices()); } + al.actionPerformed(e); }; } @@ -576,12 +568,7 @@ public class ArrayEditor extends JPanel implements PropertyEditor { } public boolean areAllSelected() { - for (int i = 0; i < elementList.getModel().getSize(); i++) { - if (!elementList.isSelectedIndex(i)) { - return false; - } - } - return true; + return elementList.getSelectedIndices().length == elementList.getModel().getSize(); } /** @@ -647,7 +634,6 @@ public class ArrayEditor extends JPanel implements PropertyEditor { 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; diff --git a/src/main/java/eva2/gui/editor/ObjectArrayEditor.java b/src/main/java/eva2/gui/editor/ObjectArrayEditor.java new file mode 100644 index 00000000..b54e969b --- /dev/null +++ b/src/main/java/eva2/gui/editor/ObjectArrayEditor.java @@ -0,0 +1,213 @@ +package eva2.gui.editor; + +import eva2.gui.*; +import eva2.tools.StringTools; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyEditor; +import java.lang.reflect.Array; + +/** + * Created by halfdan on 17/12/15. + */ +public class ObjectArrayEditor extends JPanel implements PropertyEditor { + private T[] value; + private JList objectList; + private DefaultListModel listModel; + private PropertyChangeSupport propChangeSupport; + + public ObjectArrayEditor(Class type) { + listModel = new DefaultListModel<>(); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridwidth = 2; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; + c.weighty = 0.0; + c.gridx = 0; + c.gridy = 0; + + TypeSelector typeSelector = new TypeSelector(); + typeSelector.updateClassType(type.getName()); + + add(typeSelector, c); + + JButton addButton = new JButton("Add"); + c.gridwidth = 1; + c.gridx = 2; + c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; + c.weighty = 0.0; + add(addButton, c); + + JButton removeButton = new JButton("Remove"); + c.gridx = 2; + c.gridy = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; + c.weighty = 0.0; + add(removeButton, c); + + JButton configButton; + configButton = new JButton("Config"); + c.gridx = 2; + c.gridy = 2; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; + c.weighty = 0.0; + + add(configButton, c); + + objectList = new JList<>(listModel); + objectList.setVisibleRowCount(10); + + c.gridwidth = 2; + c.gridheight = 5; + c.gridx = 0; + c.gridy = 1; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + + JScrollPane scrollPane = new JScrollPane(objectList); + add(scrollPane, c); + + addButton.addActionListener(event -> { + String className = ((Item) typeSelector.getSelectedItem()).getId(); + try { + T n = (T) Class.forName(className).newInstance(); + listModel.addElement(n); + propChangeSupport.firePropertyChange("", null, null); + } catch (Exception ex) { + System.err.println("Exception in itemStateChanged " + ex.getMessage()); + System.err.println("Classpath is " + System.getProperty("java.class.path")); + ex.printStackTrace(); + + JOptionPane.showMessageDialog(this, + "Could not create an example of\n" + + className + "\n" + + "from the current classpath. Is the resource folder at the right place?\nIs the class abstract or the default constructor missing?", + "GenericObjectEditor", + JOptionPane.ERROR_MESSAGE); + } + }); + + removeButton.addActionListener(event -> { + if (!objectList.isSelectionEmpty()) { + listModel.remove(objectList.getSelectedIndex()); + propChangeSupport.firePropertyChange("", null, null); + } + }); + + configButton.addActionListener(event -> { + T selected = objectList.getSelectedValue(); + PropertyEditor editor = PropertyEditorProvider.findEditor(selected.getClass()); + editor.setValue(selected); + PropertyDialog propertyDialog = new PropertyDialog(null, editor, StringTools.cutClassName(editor.getClass().getName())); + propertyDialog.setPreferredSize(new Dimension(500, 300)); + propertyDialog.setModal(true); + propertyDialog.setVisible(true); + propChangeSupport.firePropertyChange("", null, null); + }); + } + + @Override + public void setValue(Object value) { + this.value = (T[])value; + listModel.removeAllElements(); + for(T i : this.value) { + listModel.addElement(i); + } + } + + @Override + public Object getValue() { + if (listModel == null) { + return null; + } + if (true == false) { + return true; + } else { + // Convert the listmodel to an array of strings and return it. + int length = listModel.getSize(); + Object result = Array.newInstance(value.getClass().getComponentType(), length); + for (int i = 0; i < length; i++) { + Array.set(result, i, listModel.elementAt(i)); + } + return result; + } + } + + @Override + public boolean isPaintable() { + return true; + } + + @Override + public void paintValue(Graphics gfx, Rectangle box) { + FontMetrics fm = gfx.getFontMetrics(); + int vpad = (box.height - fm.getAscent()) / 2; + String rep; + if (listModel.getSize() == 0) { + rep = "Empty"; + } else { + rep = listModel.getSize() + " of " + StringTools.cutClassName(value.getClass().getComponentType().getName()); + Object maybeName = BeanInspector.callIfAvailable(listModel.get(0), "getName", new Object[]{}); + if (maybeName != null) { + rep = rep + " (" + maybeName + "...)"; + } + } + gfx.drawString(rep, 2, fm.getHeight() + vpad - 3); + } + + @Override + public String getJavaInitializationString() { + return null; + } + + @Override + public String getAsText() { + return null; + } + + @Override + public void setAsText(String text) throws IllegalArgumentException { + throw new IllegalArgumentException(text); + } + + @Override + public String[] getTags() { + return new String[0]; + } + + @Override + public Component getCustomEditor() { + return this; + } + + @Override + public boolean supportsCustomEditor() { + return true; + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener l) { + if (propChangeSupport == null) { + propChangeSupport = new PropertyChangeSupport(this); + } + propChangeSupport.addPropertyChangeListener(l); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener l) { + if (propChangeSupport == null) { + propChangeSupport = new PropertyChangeSupport(this); + } + propChangeSupport.removePropertyChangeListener(l); + } +} diff --git a/src/main/java/eva2/tools/Primitives.java b/src/main/java/eva2/tools/Primitives.java new file mode 100644 index 00000000..3c271c1d --- /dev/null +++ b/src/main/java/eva2/tools/Primitives.java @@ -0,0 +1,33 @@ +package eva2.tools; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by halfdan on 17/12/15. + */ +public class Primitives { + public static Class unwrap(Class clazz) { + return getWrapperTypes().get(clazz); + } + + public static boolean isWrapperType(Class clazz) + { + return getWrapperTypes().containsKey(clazz); + } + + private static Map, Class> getWrapperTypes() + { + Map, Class> ret = new HashMap<>(); + ret.put(Boolean.class, boolean.class); + ret.put(Character.class, char.class); + ret.put(Byte.class, byte.class); + ret.put(Short.class, short.class); + ret.put(Integer.class, int.class); + ret.put(Long.class, long.class); + ret.put(Float.class, float.class); + ret.put(Double.class, double.class); + ret.put(Void.class, void.class); + return ret; + } +}