views:

1303

answers:

5

I have a model class that stores keys and values:

public class KeyValue {

    private Object key;
    private String value;

    KeyValue () {
    }

    KeyValue (Object key, String value) {
        this.key=key;
        this.value=value;
    }

    public Object getKey() {
        return this.key;
    }
    public void setKey(Object key) {
        this.key=key;
    }

    public String getValue() {
        return this.value;
    }
    public void setValue(String value) {
        this.value=value;
    }

    @Override
    public String toString() {
        return this.value;
    }

}

I use this class to populate a JComboBox's Model:

for (int i = 0; i < universes.length; i++) {
    ComboBox_Universes.addItem(new KeyValue(infoObject.ID,infoObject.title));
}

I would like to refactor this logic to use a Java collection class (call it KeyValueCollection) that can support two objectives:

1) the KeyValueCollection can be used to populate the JComboBox's Model. Something like:

//get a KeyValueCollection filled with data from helper class
KeyValueCollection universeCollection = Repository.getUniverseCollection();

//use this collection as the JComboBox's model
ComboBox_Universes.setModel(universeCollection);

2) I can use the KeyValueCollection to convert a key to a value:

//ID retrieve from another control
int universeID = (int)this.Table_Values.getModel().getValueAt(row, COLUMN_ID);

//convert ID to name
String universeName = universeCollection.get(universeID).getValue();

In the .NET world, I would use the KeyedCollection class for this, but I'm not very familiar with Java.

Help is greatly appreciated.

A: 

The Map (implementation HashMap) is a Key-Value class.

It converts from key to value using the method #get.

There are also method to access all keys, all values and so on. So you should have no problem to fill a model with it.

It also contains a Key-Value pair that is called Map.Entry.

KLE
A: 

What about java.util.Map implementations?

with HashMap, for example, you can have:

Map<Object, String> map = new HashMap<Object, String>();
map.put(key, value);
Object value = map.get(key);

However, you can't directly populate the JComboBox with the Map. You can add all keys to the JComboBox, and then get the corresponding values when needed. Adding can be done in many ways, two of which:

  • new JComboBox(map.keySet().toArray(new Object[]));
  • by a loop:

    for (Object key : map.keySet() {
        comboBox.addItem(key);
    }
    
Bozho
A: 

I think that a plain HashMap<Object,String> can address most of your needs:

// Build the map
Map<Object,String> map = new HashMap<Object,String>();
for(InfoObject io : universes) 
   map.put(io.ID,io.title);


// Populate the ComboBox
for(String s : map.values())
   ComboBox_Universes.addItem(s);


// Convert ID to name
int universeID = (int)this.Table_Values.getModel().getValueAt(row, COLUMN_ID);
String universeName = map.get(universeID);
Itay
+1  A: 

Your second requirement suggests that you want a Map, but ComboboxModel is a ListModel, which suggests that you'll want to be able to efficiently retrieve elements by "index".

I don't believe any of the standard collections can do this for you as simply as you'd like. You can either create a Map, and then copy the values to a separate List/ComboboxModel, or you could use something like IndexedList (a List implementation that maintains an index Map).

Laurence Gonsalves
+1  A: 

You can use a custom class like this one (run main function to see its behavior) :

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;

public class KeyValueComboboxModel extends AbstractListModel implements ComboBoxModel, Map<String, String> {

    private TreeMap<String,String> values = new TreeMap<String,String>();

    private Map.Entry<String, String> selectedItem = null;

    public Object getSelectedItem() {
        return selectedItem;
    }

    public void setSelectedItem(Object anItem) {
        this.selectedItem = (java.util.Map.Entry<String, String>) anItem;
        fireContentsChanged(this, -1, -1);
    }

    public Object getElementAt(int index) {
        List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(values.entrySet());
        return list.get(index);
    }



    public int getSize() {
        return values.size();
    }

    public void clear() {
        values.clear();
    }

    public boolean containsKey(Object key) {
        return values.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return values.containsValue(value);
    }

    public Set<java.util.Map.Entry<String, String>> entrySet() {
        return values.entrySet();
    }

    public String get(Object key) {
        return values.get(key);
    }

    public Set<String> keySet() {
        return values.keySet();
    }

    public String put(String key, String value) {
        return values.put(key, value);
    }

    public String remove(Object key) {
        return values.remove(key);
    }

    public int size() {
        return values.size();
    }

    public Collection<String> values() {
        return values.values();
    }

    public boolean isEmpty() {
        return values.isEmpty();
    }

    public void putAll(Map<? extends String, ? extends String> m) {
        values.putAll(m);
    }


    private static String entryToString(Map.Entry<String, String> entry) {
        String str = "" + entry.getKey() + "->" + entry.getValue();
        return str;
    }

    public static void main(String[] args) {

        Map<String,String> map= new HashMap<String,String>(){{
            put("1","blue");
            put("2","red");
            put("3","white");
            put("4","black");
        }};

        JFrame f = new JFrame();
        f.setContentPane(new JPanel(new BorderLayout()));

        KeyValueComboboxModel model = new KeyValueComboboxModel();
        model.putAll(map);

        final JComboBox combo = new JComboBox(model);
        combo.setRenderer(new DefaultListCellRenderer(){

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index,
                    boolean isSelected, boolean cellHasFocus) {
                if(value instanceof Map.Entry){
                    Map.Entry<String,String> entry = (java.util.Map.Entry<String, String>) value;
                    String str = entryToString(entry);
                    return super.getListCellRendererComponent(list, str, index, isSelected, cellHasFocus);
                }
                return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            }

        });

        final JLabel lab = new JLabel("Nothing selected");

        combo.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e) {
                if(combo.getSelectedItem()!=null){
                    lab.setText(entryToString((java.util.Map.Entry<String, String>) combo.getSelectedItem()));
                } else {
                    lab.setText("");
                }

            }

        });

        f.getContentPane().add(combo,BorderLayout.CENTER);
        f.getContentPane().add(lab,BorderLayout.SOUTH);

        f.setSize(300,80);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo(null);
        f.setVisible(true);


    }


}

EDIT : to handle the selected item and keys, you may add these methods:

public void setSelectedKey(String key){
    selectedItem = values.ceilingEntry(key);
    setSelectedItem(key);
}

public void setSelectedItem(String key, String value){
    values.put(key, value);
    setSelectedKey(key);
}

By default, values are ordered following the natural order of the keys (alphabetical order of the keys, here, because these are String). If you need an other ordering, add a java.util.Comparator to the TreeMap (see TreeMap documentation).

Laurent K
Three questions:1. What is the syntax for setting the SelectedItem? The Foo part of this is [foo]ling me...Sorry.Map.Entry<String,String> item = new Foo<String,String>("2","red");model.setSelectedItem(item);2. If I wanted a setSelectedKey() method, then I would need to find the correct Map.Entry from the TreeMap, then call setSelectedItem, correct?3. Ideally, the combobox's values would be sorted alphabetically. I'm assuming that I need to sort the HashMap's values. Is there another, simpler way?
Craig
@Craig : I hope my edit does answer your needs.
Laurent K
@Laurent: Shouldn't the setSelectedKey() method read:public void setSelectedKey(String key) { //call the method that sets the variable and fires the event setSelectedItem( values.ceilingEntry(key) );}
Craig