Hi Florian,

I have several comment about your patch:
1. Run an attached test, enter some text (e.g. "aa") and press enter. The text disappears, but if you reenter text it stays.

2. The following changes in the javax.swing.plaf.basic.BasicComboBoxEditor#getItem() looks strange for me, could you describe them please?
+        if (oldValue instanceof String) {
+            return (E) newValue;
+        } else {...

BTW: The code "Method method = cls.getMethod("valueOf", new Class[]{String.class});" and other stuff like that were added with the fix of CR 4239610 (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4239610). That code looks strange for me, I believe the check "newValue.equals(oldValue.toString())" is enough for that bug... Moreover the "valueof" method cannot be used as a universal solution for String->AnyClass conversion because of that's impossible to use in case oldValue is null.

Therefore I think we should not change the WindowsComboBoxUI.java, ComboBoxEditor.java, BasicComboBoxEditor.java and MetalComboBoxEditor.java files at all, because there are no available API for converting string into *any* class. What do you think about that? Logically it means that editor can edit only strings but not any class.

3. After item 2 we will see that it's not possible to generify the set/getSelectedItem methods in the javax.swing.ComboBoxModel interface because of some code in the javax.swing.JComboBox#actionPerformed method. The same set/getSelectedItem methods in the JComboBox class shouldn't be generified as well. That's also correspondent to my old proposal:
http://mail.openjdk.java.net/pipermail/swing-dev/2009-December/000878.html

4. As the result of item 3 we can generify javax.swing.JComboBox#dataModel like this:
protected ComboBoxModel<? extends E>    dataModel;

I remember our discussion about JList model generification:
http://mail.openjdk.java.net/pipermail/swing-dev/2009-March/000443.html

So we can improve JList generification as well (it's possible to do without backward incompatibility because of JList generification is a jdk7 feature)

5. Some regression tests are needed (like this one http://hg.openjdk.java.net/jdk7/swing/jdk/file/7bcb1864f424/test/javax/swing/JList/6823603/bug6823603.java). We should write them after generification will be fully discussed and coordinated.

I attached a raw patch that contains all changes that I described above. What do you think about this way of generification?

Regards, Pavel

Hi Pavel,

here is a patch to "generify" JComboBox along with: ComboBoxEditor, 
ComboBoxModel,
DefaultComboBoxModel and MutableComboBoxModel.

I fixed the BasicComboBoxEditor as I proposed in my last post.

MutableComboBoxModel.removeElement is not generified currently and thus still 
takes an Object as argument. This is consistent with the Collection Framework 
(eg. java.util.List.remove). Please tell me if you prefer this method to take a 
generic parameter as well.


I also created a new issue in the OpenJDK Bugzilla system:
https://bugs.openjdk.java.net/show_bug.cgi?id=100153

Regards,
Florian

package crs.issue100153;

import javax.swing.*;

public class Test {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JComboBox<Integer> comboBox = new JComboBox<Integer>(new 
Integer[]{1, 2, 666});

                comboBox.setEditable(true);

                JPanel content = new JPanel();

                content.add(comboBox);

                JFrame frame = new JFrame();

                frame.setContentPane(content);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}
--- old/src/share/classes/javax/swing/ComboBoxModel.java	2010-11-23 13:51:19.984375000 +0300
+++ new/src/share/classes/javax/swing/ComboBoxModel.java	2010-11-23 13:51:18.843750000 +0300
@@ -33,9 +33,11 @@
  * <code>ListModel</code>. This disjoint behavior allows for the temporary
  * storage and retrieval of a selected item in the model.
  *
+ * @param <E> the type of the elements of this model
+ *
  * @author Arnaud Weber
  */
-public interface ComboBoxModel extends ListModel {
+public interface ComboBoxModel<E> extends ListModel<E> {
 
   /**
    * Set the selected item. The implementation of this  method should notify
--- old/src/share/classes/javax/swing/DefaultComboBoxModel.java	2010-11-23 13:51:24.718750000 +0300
+++ new/src/share/classes/javax/swing/DefaultComboBoxModel.java	2010-11-23 13:51:23.703125000 +0300
@@ -44,19 +44,21 @@
 /**
  * The default model for combo boxes.
  *
+ * @param <E> the type of the elements of this model
+ *
  * @author Arnaud Weber
  * @author Tom Santos
  */
 
-public class DefaultComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable {
-    Vector objects;
+public class DefaultComboBoxModel<E> extends AbstractListModel<E> implements MutableComboBoxModel<E>, Serializable {
+    Vector<E> objects;
     Object selectedObject;
 
     /**
      * Constructs an empty DefaultComboBoxModel object.
      */
     public DefaultComboBoxModel() {
-        objects = new Vector();
+        objects = new Vector<E>();
     }
 
     /**
@@ -65,8 +67,8 @@
      *
      * @param items  an array of Object objects
      */
-    public DefaultComboBoxModel(final Object items[]) {
-        objects = new Vector();
+    public DefaultComboBoxModel(final E items[]) {
+        objects = new Vector<E>();
         objects.ensureCapacity( items.length );
 
         int i,c;
@@ -84,7 +86,7 @@
      *
      * @param v  a Vector object ...
      */
-    public DefaultComboBoxModel(Vector<?> v) {
+    public DefaultComboBoxModel(Vector<E> v) {
         objects = v;
 
         if ( getSize() > 0 ) {
@@ -117,7 +119,7 @@
     }
 
     // implements javax.swing.ListModel
-    public Object getElementAt(int index) {
+    public E getElementAt(int index) {
         if ( index >= 0 && index < objects.size() )
             return objects.elementAt(index);
         else
@@ -136,7 +138,7 @@
     }
 
     // implements javax.swing.MutableComboBoxModel
-    public void addElement(Object anObject) {
+    public void addElement(E anObject) {
         objects.addElement(anObject);
         fireIntervalAdded(this,objects.size()-1, objects.size()-1);
         if ( objects.size() == 1 && selectedObject == null && anObject != null ) {
@@ -145,7 +147,7 @@
     }
 
     // implements javax.swing.MutableComboBoxModel
-    public void insertElementAt(Object anObject,int index) {
+    public void insertElementAt(E anObject,int index) {
         objects.insertElementAt(anObject,index);
         fireIntervalAdded(this, index, index);
     }
--- old/src/share/classes/javax/swing/JComboBox.java	2010-11-23 13:51:29.953125000 +0300
+++ new/src/share/classes/javax/swing/JComboBox.java	2010-11-23 13:51:28.937500000 +0300
@@ -69,6 +69,8 @@
  * @see ComboBoxModel
  * @see DefaultComboBoxModel
  *
+ * @param <E> the type of the elements of this combo box
+ *
  * @beaninfo
  *   attribute: isContainer false
  * description: A combination of a text field and a drop-down list.
@@ -76,7 +78,7 @@
  * @author Arnaud Weber
  * @author Mark Davidson
  */
-public class JComboBox extends JComponent
+public class JComboBox<E> extends JComponent
 implements ItemSelectable,ListDataListener,ActionListener, Accessible {
     /**
      * @see #getUIClassID
@@ -91,7 +93,7 @@
      * @see #getModel
      * @see #setModel
      */
-    protected ComboBoxModel    dataModel;
+    protected ComboBoxModel<? extends E>    dataModel;
     /**
      * This protected field is implementation specific. Do not access directly
      * or override. Use the accessor methods instead.
@@ -99,7 +101,7 @@
      * @see #getRenderer
      * @see #setRenderer
      */
-    protected ListCellRenderer renderer;
+    protected ListCellRenderer<? super E> renderer;
     /**
      * This protected field is implementation specific. Do not access directly
      * or override. Use the accessor methods instead.
@@ -156,7 +158,7 @@
      */
     protected Object selectedItemReminder = null;
 
-    private Object prototypeDisplayValue;
+    private E prototypeDisplayValue;
 
     // Flag to ensure that infinite loops do not occur with ActionEvents.
     private boolean firingActionEvent = false;
@@ -175,7 +177,7 @@
      *          displayed list of items
      * @see DefaultComboBoxModel
      */
-    public JComboBox(ComboBoxModel aModel) {
+    public JComboBox(ComboBoxModel<? extends E> aModel) {
         super();
         setModel(aModel);
         init();
@@ -189,9 +191,9 @@
      * @param items  an array of objects to insert into the combo box
      * @see DefaultComboBoxModel
      */
-    public JComboBox(final Object items[]) {
+    public JComboBox(final E items[]) {
         super();
-        setModel(new DefaultComboBoxModel(items));
+        setModel(new DefaultComboBoxModel<E>(items));
         init();
     }
 
@@ -203,9 +205,9 @@
      * @param items  an array of vectors to insert into the combo box
      * @see DefaultComboBoxModel
      */
-    public JComboBox(Vector<?> items) {
+    public JComboBox(Vector<E> items) {
         super();
-        setModel(new DefaultComboBoxModel(items));
+        setModel(new DefaultComboBoxModel<E>(items));
         init();
     }
 
@@ -219,7 +221,7 @@
      */
     public JComboBox() {
         super();
-        setModel(new DefaultComboBoxModel());
+        setModel(new DefaultComboBoxModel<E>());
         init();
     }
 
@@ -263,7 +265,7 @@
     public void updateUI() {
         setUI((ComboBoxUI)UIManager.getUI(this));
 
-        ListCellRenderer renderer = getRenderer();
+        ListCellRenderer<? super E> renderer = getRenderer();
         if (renderer instanceof Component) {
             SwingUtilities.updateComponentTreeUI((Component)renderer);
         }
@@ -302,8 +304,8 @@
      *        bound: true
      *  description: Model that the combo box uses to get data to display.
      */
-    public void setModel(ComboBoxModel aModel) {
-        ComboBoxModel oldModel = dataModel;
+    public void setModel(ComboBoxModel<? extends E> aModel) {
+        ComboBoxModel<? extends E> oldModel = dataModel;
         if (oldModel != null) {
             oldModel.removeListDataListener(this);
         }
@@ -322,7 +324,7 @@
      * @return the <code>ComboBoxModel</code> that provides the displayed
      *                  list of items
      */
-    public ComboBoxModel getModel() {
+    public ComboBoxModel<? extends E> getModel() {
         return dataModel;
     }
 
@@ -461,8 +463,8 @@
      *     expert: true
      *  description: The renderer that paints the item selected in the list.
      */
-    public void setRenderer(ListCellRenderer aRenderer) {
-        ListCellRenderer oldRenderer = renderer;
+    public void setRenderer(ListCellRenderer<? super E> aRenderer) {
+        ListCellRenderer<? super E> oldRenderer = renderer;
         renderer = aRenderer;
         firePropertyChange( "renderer", oldRenderer, renderer );
         invalidate();
@@ -475,7 +477,7 @@
      * @return  the <code>ListCellRenderer</code> that displays
      *                  the selected item.
      */
-    public ListCellRenderer getRenderer() {
+    public ListCellRenderer<? super E> getRenderer() {
         return renderer;
     }
 
@@ -640,7 +642,7 @@
     public int getSelectedIndex() {
         Object sObject = dataModel.getSelectedItem();
         int i,c;
-        Object obj;
+        E obj;
 
         for ( i=0,c=dataModel.getSize();i<c;i++ ) {
             obj = dataModel.getElementAt(i);
@@ -658,7 +660,7 @@
      * @see #setPrototypeDisplayValue
      * @since 1.4
      */
-    public Object getPrototypeDisplayValue() {
+    public E getPrototypeDisplayValue() {
         return prototypeDisplayValue;
     }
 
@@ -683,7 +685,7 @@
      *   attribute: visualUpdate true
      * description: The display prototype value, used to compute display width and height.
      */
-    public void setPrototypeDisplayValue(Object prototypeDisplayValue) {
+    public void setPrototypeDisplayValue(E prototypeDisplayValue) {
         Object oldValue = this.prototypeDisplayValue;
         this.prototypeDisplayValue = prototypeDisplayValue;
         firePropertyChange("prototypeDisplayValue", oldValue, prototypeDisplayValue);
@@ -708,12 +710,12 @@
      *   }
      * </pre>
      *
-     * @param anObject the Object to add to the list
+     * @param item the item to add to the list
      * @see MutableComboBoxModel
      */
-    public void addItem(Object anObject) {
+    public void addItem(E item) {
         checkMutableComboBoxModel();
-        ((MutableComboBoxModel)dataModel).addElement(anObject);
+        ((MutableComboBoxModel<E>)dataModel).addElement(item);
     }
 
     /**
@@ -721,14 +723,14 @@
      * This method works only if the <code>JComboBox</code> uses a
      * mutable data model.
      *
-     * @param anObject the <code>Object</code> to add to the list
+     * @param item the item to add to the list
      * @param index    an integer specifying the position at which
      *                  to add the item
      * @see MutableComboBoxModel
      */
-    public void insertItemAt(Object anObject, int index) {
+    public void insertItemAt(E item, int index) {
         checkMutableComboBoxModel();
-        ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index);
+        ((MutableComboBoxModel<E>)dataModel).insertElementAt(item,index);
     }
 
     /**
@@ -736,12 +738,12 @@
      * This method works only if the <code>JComboBox</code> uses a
      * mutable data model.
      *
-     * @param anObject  the object to remove from the item list
+     * @param item  the item to remove from the item list
      * @see MutableComboBoxModel
      */
-    public void removeItem(Object anObject) {
+    public void removeItem(E item) {
         checkMutableComboBoxModel();
-        ((MutableComboBoxModel)dataModel).removeElement(anObject);
+        ((MutableComboBoxModel<E>)dataModel).removeElement(item);
     }
 
     /**
@@ -756,7 +758,7 @@
      */
     public void removeItemAt(int anIndex) {
         checkMutableComboBoxModel();
-        ((MutableComboBoxModel)dataModel).removeElementAt( anIndex );
+        ((MutableComboBoxModel<E>)dataModel).removeElementAt( anIndex );
     }
 
     /**
@@ -764,15 +766,15 @@
      */
     public void removeAllItems() {
         checkMutableComboBoxModel();
-        MutableComboBoxModel model = (MutableComboBoxModel)dataModel;
+        MutableComboBoxModel<E> model = (MutableComboBoxModel<E>)dataModel;
         int size = model.getSize();
 
         if ( model instanceof DefaultComboBoxModel ) {
-            ((DefaultComboBoxModel)model).removeAllElements();
+            ((DefaultComboBoxModel<E>)model).removeAllElements();
         }
         else {
             for ( int i = 0; i < size; ++i ) {
-                Object element = model.getElementAt( 0 );
+                E element = model.getElementAt( 0 );
                 model.removeElement( element );
             }
         }
@@ -1188,11 +1190,11 @@
 
 
     private static class ComboBoxActionPropertyChangeListener
-                 extends ActionPropertyChangeListener<JComboBox> {
-        ComboBoxActionPropertyChangeListener(JComboBox b, Action a) {
+                 extends ActionPropertyChangeListener<JComboBox<?>> {
+        ComboBoxActionPropertyChangeListener(JComboBox<?> b, Action a) {
             super(b, a);
         }
-        protected void actionPropertyChanged(JComboBox cb,
+        protected void actionPropertyChanged(JComboBox<?> cb,
                                              Action action,
                                              PropertyChangeEvent e) {
             if (AbstractAction.shouldReconfigure(e)) {
@@ -1454,10 +1456,10 @@
      *
      * @param index  an integer indicating the list position, where the first
      *               item starts at zero
-     * @return the <code>Object</code> at that list position; or
+     * @return the item at that list position; or
      *                  <code>null</code> if out of range
      */
-    public Object getItemAt(int index) {
+    public E getItemAt(int index) {
         return dataModel.getElementAt(index);
     }
 
@@ -1491,11 +1493,11 @@
          * @return an int equal to the selected row, where 0 is the
          *         first item and -1 is none.
          */
-        int selectionForKey(char aKey,ComboBoxModel aModel);
+        int selectionForKey(char aKey,ComboBoxModel<?> aModel);
     }
 
     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
-        public int selectionForKey(char aKey,ComboBoxModel aModel) {
+        public int selectionForKey(char aKey,ComboBoxModel<?> aModel) {
             int i,c;
             int currentSelection = -1;
             Object selectedItem = aModel.getSelectedItem();
@@ -1616,7 +1618,7 @@
     implements AccessibleAction, AccessibleSelection {
 
 
-        private JList popupList; // combo box popup list
+        private JList<?> popupList; // combo box popup list
         private Accessible previousSelectedAccessible = null;
 
         /**
--- old/src/share/classes/javax/swing/MutableComboBoxModel.java	2010-11-23 13:51:35.765625000 +0300
+++ new/src/share/classes/javax/swing/MutableComboBoxModel.java	2010-11-23 13:51:34.609375000 +0300
@@ -30,16 +30,16 @@
  * @author Tom Santos
  */
 
-public interface MutableComboBoxModel extends ComboBoxModel {
+public interface MutableComboBoxModel<E> extends ComboBoxModel<E> {
 
     /**
      * Adds an item at the end of the model. The implementation of this method
      * should notify all registered <code>ListDataListener</code>s that the
      * item has been added.
      *
-     * @param obj the <code>Object</code> to be added
+     * @param item the item to be added
      */
-    public void addElement( Object obj );
+    public void addElement( E item );
 
     /**
      * Removes an item from the model. The implementation of this method should
@@ -55,17 +55,17 @@
      * should notify all registered <code>ListDataListener</code>s that the
      * item has been added.
      *
-     * @param obj  the <code>Object</code> to be added
+     * @param item  the item to be added
      * @param index  location to add the object
      */
-    public void insertElementAt( Object obj, int index );
+    public void insertElementAt( E item, int index );
 
     /**
      * Removes an item at a specific index. The implementation of this method
      * should notify all registered <code>ListDataListener</code>s that the
      * item has been removed.
      *
-     * @param index  location of object to be removed
+     * @param index  location of the item to be removed
      */
     public void removeElementAt( int index );
 }

Reply via email to