ComboBoxModel with Dynamic Proxied Objects
18:04 28 Jul 2013

As we know the ComboBoxModel interface is used to created a class in witch we can specify how to relate a collections of objects (the model) with the combobox, basically by supplying the necessary "information" on how to retrieve items and set current item. Generally I wrote those classes declaring as member a Collection , and just delegate some functionality to the collection object in the implemented methods. When the actual class of all the contained object are a Not-Proxied object everything goes fine (surely 90% of the times we have this situation), but this time a face the fact to have references to Proxied Objects and things goes strangely wrong. The JComboBox behavior goes wrong as it cannot change the current selection.

I am trying to get some more information, but by now I only know that the method setSelectedItem of ComboBoxModel interface, that any concrete classes implements is not invoking when there are proxied objects around. That is my question: What is happening and, more important, is it fixable?

I leave an example, ready to look and see for yourself.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

/**
 *
 * @author Administrador
 */
public class AComboBoxWithProxyProblem extends JFrame implements ActionListener
{
     ComboBoxModel modelWithProxies = new ItemComboBoxModelWithProxies();
     ComboBoxModel modelWithoutProxies = new ItemComboBoxModelWithoutProxies();
     
        public AComboBoxWithProxyProblem()
        {
            final JComboBox comboBox = new JComboBox();
            comboBox.addActionListener( this );
            
            
            getContentPane().setLayout(new BoxLayout(this.getContentPane(),BoxLayout.LINE_AXIS));
            getContentPane().add(comboBox);
            
            JRadioButton btnProxy = new JRadioButton("Proxy model");
            btnProxy.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e)
                {
                    comboBox.setModel(modelWithProxies);
                    comboBox.setSelectedIndex(0);
                }
            });
            
            getContentPane().add(btnProxy);
            
            JRadioButton btnNoProxy = new JRadioButton("Non Proxy model");
            btnNoProxy.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e)
                {
                    comboBox.setModel(modelWithoutProxies);
                    comboBox.setSelectedIndex(0);
                }
            });
            
            getContentPane().add(btnNoProxy);
            
            ButtonGroup group = new ButtonGroup();
            group.add(btnProxy);
            group.add(btnNoProxy);
            
            setTitle("Mmmm...");
        }

        @Override
        public void actionPerformed(ActionEvent e)
        {
            JComboBox comboBox = (JComboBox)e.getSource();
            Item item = (Item)comboBox.getSelectedItem();
            System.out.println("[actionPerformed] - " + item.getId() + " : " + item.getDescription() );
        }


    interface ItemInterface
    {

        String getDescription();

        int getId();

        @Override
        String toString();

    }

    class Item implements AComboBoxWithProxyProblem.ItemInterface
    {
        private int id;
        private String description;

        public Item(int id, String description)
        {
            this.id = id;
            this.description = description;
        }

        @Override
        public int getId()
        {
            return id;
        }

        @Override
        public String getDescription()
        {
            return description;
        }

        @Override
        public String toString()
        {
            return description;
        }

    }
        
    private class ItemComboBoxModelWithoutProxies extends AbstractListModel implements ComboBoxModel
    {
        List foos;
        ItemInterface selected;
        
        public ItemComboBoxModelWithoutProxies()
        {
            foos = new ArrayList<> ();
            foos.add(new Item(1,"One"));
            foos.add(new Item(2,"Two"));
            foos.add(new Item(3,"Three"));
        }

        @Override
        public Object getSelectedItem()
        {
            return selected;
        }

        @Override
        public void setSelectedItem(Object tournament)
        {
            System.out.println("[setSelectedItem] " + tournament);
            selected = (ItemInterface) tournament;
        }

        @Override
        public int getSize()
        {
            return this.foos.size();
        }

        @Override
        public Object getElementAt(int i)
        {
            return this.foos.get(i);
        }
    }
    
    private class ItemComboBoxModelWithProxies extends AbstractListModel implements ComboBoxModel
    {
        List foos;
        Object selected;
        
        public ItemComboBoxModelWithProxies()
        {
            foos = new ArrayList<> ();
            ItemInterface item;
            item = (ItemInterface) Proxy.newProxyInstance(Item.class.getClassLoader(),
                    Item.class.getInterfaces(),
                    new ItemInvocationHandler (new Item(1,"One")));
            foos.add(item);
            
            item = (ItemInterface) Proxy.newProxyInstance(Item.class.getClassLoader(),
                    Item.class.getInterfaces(),
                    new ItemInvocationHandler (new Item(2,"Two")));
            foos.add(item);
            
            item = (ItemInterface) Proxy.newProxyInstance(Item.class.getClassLoader(),
                    Item.class.getInterfaces(),
                    new ItemInvocationHandler (new Item(3,"Three")));
            foos.add(item);
        }

        @Override
        public Object getSelectedItem()
        {
            return selected;
        }

        @Override
        public void setSelectedItem(Object tournament)
        {
            System.out.println("[setSelectedItem] " + tournament);
            selected = (ItemInterface) tournament;
        }

        @Override
        public int getSize()
        {
            return this.foos.size();
        }

        @Override
        public Object getElementAt(int i)
        {
            return this.foos.get(i);
        }
        
        private class ItemInvocationHandler implements InvocationHandler {
            Item item;

            public ItemInvocationHandler(Item item)
            {
                this.item = item;
            }
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
            {
                return method.invoke(this.item, args);
            }
        }
    }
    
    public static void main(String[] args)
    {
        JFrame frame = new AComboBoxWithProxyProblem();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible( true );
    }

}
java swing comboboxmodel