
Question:
I'm overwhelmed with attempting to design a solution for this problem - it's a by-product of inexperience.
My goal is to read an XML input file, store the information from the XML and populate two combo boxes with data from the XML. The content of the second combo box will change based on the selection in the first.
Given this XML structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Category xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Node>
<ID>Unique string</ID>
<Name>Unique string</Name>
<Code>Generic string<Code>
<Kind>Generic string</Kind>
<Frame>Generic string</Frame>
...
</Node>
...
</Category>
First combo box: Must contain only the unique values found in the the Kind section.
Second combo box: Contains ALL of the Name entries from every Node whose Kind equals the Kind selected in the first combo box.
Regarding the XML source: It is externally maintained and generated. The values in the ID section are always going to be unique. The values in the Name section are always going to be unique. The schema will (supposedly) never change. New unique values may appear in the Kind section in the future.
My proposed solution: Create a class XMLNode to represent a Node from the XML source. Members of class XMLNode correspond to the tags in each Node. Loop through all the Nodes and create an XMLNode for each one. While looping through nodes: Add XMLNode objects in a hash map, with Keys = XMLNode.ID and vals = the XMLNode. Create an array of unique Kinds.
Populate combo box one from the array of Kind entries. Populate combo box two from the Name data for each .
Is this an appropriate approach, or have I overlooked a better/easier/more elegant solution? If I'm on the right track, are there any obvious flaws to my proposed solution?
Answer1:<ul><li>
use <a href="http://docs.oracle.com/javase/6/docs/api/javax/swing/ComboBoxModel.html" rel="nofollow">ComboBoxModel (Java6)</a> for storing Items for <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html" rel="nofollow">JComboBox</a>
</li> <li>a<a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html" rel="nofollow">ll updates to the JComboBox and its ComboBoxModel must be done on EDT</a>
</li> <li>redirect all FileIO
or Database
event to the Runnable#Thread
or SwingWorker
maybe this logics can help you,
</li> </ul>code
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class ComboBoxTwo extends JFrame implements ActionListener, ItemListener {
private static final long serialVersionUID = 1L;
private JComboBox mainComboBox;
private JComboBox subComboBox;
private Hashtable<Object, Object> subItems = new Hashtable<Object, Object>();
public ComboBoxTwo() {
String[] items = {"Select Item", "Color", "Shape", "Fruit"};
mainComboBox = new JComboBox(items);
mainComboBox.addActionListener(this);
mainComboBox.addItemListener(this);
//prevent action events from being fired when the up/down arrow keys are used
//mainComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
getContentPane().add(mainComboBox, BorderLayout.WEST);
subComboBox = new JComboBox();// Create sub combo box with multiple models
subComboBox.setPrototypeDisplayValue("XXXXXXXXXX"); // JDK1.4
subComboBox.addItemListener(this);
getContentPane().add(subComboBox, BorderLayout.EAST);
String[] subItems1 = {"Select Color", "Red", "Blue", "Green"};
subItems.put(items[1], subItems1);
String[] subItems2 = {"Select Shape", "Circle", "Square", "Triangle"};
subItems.put(items[2], subItems2);
String[] subItems3 = {"Select Fruit", "Apple", "Orange", "Banana"};
subItems.put(items[3], subItems3);
// mainComboBox.setSelectedIndex(1);
}
@Override
public void actionPerformed(ActionEvent e) {
String item = (String) mainComboBox.getSelectedItem();
Object o = subItems.get(item);
if (o == null) {
subComboBox.setModel(new DefaultComboBoxModel());
} else {
subComboBox.setModel(new DefaultComboBoxModel((String[]) o));
}
}
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
if (e.getSource() == mainComboBox) {
if (mainComboBox.getSelectedIndex() != 0) {
FirstDialog firstDialog = new FirstDialog(ComboBoxTwo.this,
mainComboBox.getSelectedItem().toString(), "Please wait, Searching for ..... ");
}
}
}
}
private class FirstDialog extends JDialog {
private static final long serialVersionUID = 1L;
FirstDialog(final Frame parent, String winTitle, String msgString) {
super(parent, winTitle);
setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
JLabel myLabel = new JLabel(msgString);
JButton bNext = new JButton("Stop Processes");
add(myLabel, BorderLayout.CENTER);
add(bNext, BorderLayout.SOUTH);
bNext.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
setVisible(false);
}
});
javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
t.setRepeats(false);
t.start();
setLocationRelativeTo(parent);
setSize(new Dimension(400, 100));
setVisible(true);
}
}
public static void main(String[] args) {
JFrame frame = new ComboBoxTwo();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Answer2:I ended up with a class NodeImporter and a class Node. Node represents a node, NodeImporter parses the XML. In the NodeImporter, the XML source is parsed and represented as a HashMap of HashMaps. The outer HashMap (Key,Value) is (Kind,(HashMap(Key,Value)). The inner HashMap (Key,Value) is (UinqueID,Node) for a final result of (Kind,(UniqueID,Node)). I call the final result "filteredMap". All of NodeImporter's fields and methods are private, except for the constructor, and a getter for filteredMap. The class that needs the data to build the combo boxes gets filteredMap from an instance of NodeImporter. It can then get the keys of the outer HashMap to build the first ComboBoxModel. It can just as easily get the inner HashMap to be used as the HashMap for the second combo box.
pseudo code for setup:
Class NodeImporter built with class members:
arrayList(String) uniqueKinds = null
arrayList(Node) allNodes = null
HashMap(String, HashMap(String,Node))) filteredNodes = null
Class NodeImporter constructor:
open XML
while xml source has next
{
add next node to allNodes, key = node.uniqueId, Val = node
if next node.kind not in uniqueKinds, add node.kind to uniqueKinds
}
ClassNodeImporter method makeFilteredeMap:
private boolean makeFilteredeMap() {
if (uniqueKinds.isEmpty()) {
return false;
} else {
for (String k : uniqueKinds) {
HashMap<String, Node> aMap = new HashMap<String, Node>();
for (Node n : allNodes) {
if (n.getKind().equals(k)) {
aMap.put(n.getCode(), n);
}
}
filteredNodes.put(k, aMap);
}
return true;
}
}