0

I created a custom model for JCombobox to synchronize it with a LinkedHashMap. I wanted to create a model that could be reused with any LinkedHashMap (hence the use of generics). When I run it with my application, the String fail to appear. When I click on where the Strings should be, the action listeners fire correctly and the selected item changes. Nevertheless, the dropdown menu does not display the String? I know it's based of a JList so do I need to do anything fancy with JLabels and such? Is that part of the model as well? Do I need to implement anything else?

Here is my custom model

import java.io.Serializable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import javax.swing.AbstractListModel;
import javax.swing.MutableComboBoxModel;

/**
 * A model design to automatically update based of a the key values of a Hashmap
 * @author Skylion
 *
 * @param <K> The generic representing key of the Hashmap
 * @param <V> The generic representing the value of the Hashmap
 */
public class HashMapComboBoxModel<K, V> extends AbstractListModel<K> implements MutableComboBoxModel<K>, 
Serializable {

    /**
     * Auto-generated
     */
    private static final long serialVersionUID = -1293826656458176439L;

    /**
     * The Selected Item
     */
    private K selectedItem;

    LinkedHashMap<K,V> data;

    public HashMapComboBoxModel(LinkedHashMap<K,V> data){
        this.data = data;
    }

    @Override
    public K getElementAt(int index) {
        List<Entry<K,V>> randAccess = new ArrayList<Entry<K,V>>((Collection<? extends Entry<K, V>>) data.entrySet());
        return randAccess.get(index).getKey();
    }

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

    @Override
    public K getSelectedItem() {
        return selectedItem;
    }

    @SuppressWarnings("unchecked")//Equals() implements the check
    @Override
    public void setSelectedItem(Object anItem) {
        for(K keys: data.keySet()){
            if(keys.equals(anItem)){
                this.selectedItem = (K) anItem;
                return;
            }
        }
    }

    @Override
    public void addElement(Object obj) {
            addElement(downcastToEntry(obj));
    }

    @SuppressWarnings("unchecked")
    private Entry<K,V> downcastToEntry(Object obj){
        if(obj instanceof Entry && obj.getClass().isAssignableFrom(
                data.entrySet().iterator().next().getClass())){
            return (Entry<K,V>)obj;
        }
        return null;
    }

    /**
     * Adds an Entry value to the hashmap
     * @param obj The Entry value you want to add
     * @return return true if added false otherwise
     */
    public boolean addElement(Entry<K,V> obj){
        if(obj == null){return false;}
        return this.data.entrySet().add(obj);
    }

    @Override
    public void insertElementAt(Object obj, int index) {
        Entry<K,V> entry = downcastToEntry(obj);
        addToMapAtIndex(index, entry.getKey(), entry.getValue());
    }

    private void addToMapAtIndex(int index, K key, V value) {
        assert (data != null);
        assert !data.containsKey(key);
        assert (index >= 0) && (index < data.size());

        int i = 0;
        List<Entry<K, V>> rest = new ArrayList<Entry<K, V>>();
        for (Entry<K, V> entry : data.entrySet()) {
            if (i++ >= index) {
                rest.add(entry);
            }
        }
        data.put(key, value);
        for (int j = 0; j < rest.size(); j++) {
            Entry<K, V> entry = rest.get(j);
            data.remove(entry.getKey());
            data.put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public void removeElement(Object obj) {
        data.remove(obj);
    }

    @Override
    public void removeElementAt(int index) {
        data.remove(getElementAt(index));
    }

}

Here is how I constructed the JCombobox.

LinkedHashMap<String, myCustomClass> map = new LinkedHashMap<String, myCustomClass(); // CODE... CODE... more code...

JCombobox box = new JCombobox<String>(new HashMapComboBoxModel<String,myCustomClass>(map));

//Later in the code

map.put("myString", myObject); //JCombobox updates, but the dropdown is whited out.

Skylion
  • 2,696
  • 26
  • 50
  • A data model is responsible for sending events if its contents changes. You are missing that part completely. – Holger Jun 03 '14 at 16:21
  • For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). – Andrew Thompson Jun 04 '14 at 02:39

1 Answers1

0

NVM, I figured it out. I need to call the fire the DataListener method. After looking at the source code for JCombobox, I need to use the add methods through the model for it to properly update. Instead of rewriting the class, I decided to simply fire the listeners on the getSize() method as a little hack. I force the entire list to update when the method is called so that when the combobox executes the for loop, it automatically updates the model.

It maybe hacky but it works!

Skylion
  • 2,696
  • 26
  • 50