0

First, let me discuss the data model :

I've compiled an HL7 definition in an tree-like object model, with 3 levels : Segment, Element and Subelement. The segment contains a List, and the Element contains a List. The relationship between those is kinda like "Continent-Country-City"

Second, i've created a GUI component composed of 3 combobox, each having a different ComboBoxModel accessing the object model described earlier.

I've applied the Observer pattern on the ComboBoxModel, so that each model is aware of which List<> it has to access :
- the HL7DefinitionElementModel observes the HL7DefinitionSegmentModel
- the HL7DefinitionSubelementModel observes the HL7DefinitionElementModel

When you pick a value in the first combobox (the Segment), the HL7DefinitionElementModel updates and store which Segment have been picked. The data is correctly stored. The first value is correctly displayed in the "closed" JComboBox. But when you try to open that JComboBox (the Element), if the number of elements is too big (around 12), the items in the invoked Popup Menu are blank.

I'll gladly post any code. I could send the entire project if needed, but haven't been able to reduce it to a SSCEE.

EDIT : here is an illustration of the problem
(http://imgur.com/AiJ8mu0)

EDIT : here is an SSCCE :

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.ListDataListener;

public class Application extends JFrame {
  /**
   * 
   */
  private static final long serialVersionUID = -2517616123689799182L;

  public Application() {
    add(new HL7FieldSelector());
  }

  public static void main(String[] args) {
    Application a = new Application();
    a.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    a.setSize(new Dimension(400, 100));
    a.setVisible(true);
  }
}

class HL7FieldSelector extends JPanel implements ActionListener {
  /**
   * 
   */
  private static final long serialVersionUID = 6135074279269049773L;
  private JComboBox<HL7DefinitionSegment> segment;
  private JComboBox<HL7DefinitionElement> element;
  private HL7DefinitionSegmentModel segmentModel;
  private HL7DefinitionElementModel elementModel;

  public HL7FieldSelector() {
    segment = new JComboBox<HL7DefinitionSegment>();
    element = new JComboBox<HL7DefinitionElement>();

    segmentModel = new HL7DefinitionSegmentModel();
    elementModel = new HL7DefinitionElementModel();

    segmentModel.addObserver(elementModel);

    segment.setModel(segmentModel);
    element.setModel(elementModel);

    segment.addActionListener(this);
    element.addActionListener(this);

    initUI();
  }

  private void initUI() {
    this.setLayout(new GridLayout(1, 3));
    this.add(segment);
    this.add(element);
    this.setMinimumSize(new Dimension(500, 30));
    this.setPreferredSize(new Dimension(500, 30));
    this.setMaximumSize(new Dimension(500, 30));

    segment.setMaximumRowCount(10);
    element.setMaximumRowCount(10);
  }

  @Override
  public void actionPerformed(ActionEvent arg0) {
    setVisible(!isVisible());
    setVisible(!isVisible());
  }
}

class HL7DefinitionSegment implements Comparable<HL7DefinitionSegment>, Serializable {
  /**
   * 
   */
  private static final long serialVersionUID = 7922798285885405647L;
  private List<HL7DefinitionElement> elements;
  private String name;

  public HL7DefinitionSegment(String name, ArrayList<HL7DefinitionElement> elements) {
    this.name = name;
    this.elements = elements;
  }

  public HL7DefinitionSegment() {
    elements = new Vector<HL7DefinitionElement>();
  }

  public boolean addElement(HL7DefinitionElement element) {
    return elements.add(element);
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public List<HL7DefinitionElement> getElements() {
    return elements;
  }

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

  @Override
  public int compareTo(HL7DefinitionSegment segment) {
    return name.compareTo(segment.getName());
  }
}

class HL7DefinitionElement implements Comparable<HL7DefinitionElement>, Serializable {
  /**
   * 
   */
  private static final long serialVersionUID = -5344721929162039227L;
  private String name;
  private String number;

  public HL7DefinitionElement(String name, String number) {
    this.name = name;
    this.number = number;
  }

  public HL7DefinitionElement() {
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getNumber() {
    return number;
  }

  public void setNumber(String number) {
    this.number = number;
  }

  @Override
  public String toString() {
    return number + " " + name;
  }

  @Override
  public int compareTo(HL7DefinitionElement element) {
    return number.compareTo(element.getNumber());
  }
}

class HL7DefinitionModel implements Serializable {
  /**
   * 
   */
  private static final long serialVersionUID = -8180582371776223436L;
  private List<HL7DefinitionSegment> segments;
  private static HL7DefinitionModel _instance;

  public HL7DefinitionModel(Vector<HL7DefinitionSegment> segments) {
    this.segments = segments;
  }

  public HL7DefinitionModel() {
    segments = new Vector<HL7DefinitionSegment>();
  }

  public boolean addSegment(HL7DefinitionSegment segment) {
    return segments.add(segment);
  }

  public List<HL7DefinitionSegment> getSegments() {
    return segments;
  }

  public void setSegments(List<HL7DefinitionSegment> segments) {
    this.segments = segments;
  }

  public static HL7DefinitionModel getInstance() {
    if (_instance == null) {
      _instance = new HL7DefinitionModel();

      for (int i = 0; i < 2; i++) {
        HL7DefinitionSegment s = new HL7DefinitionSegment();
        s.setName("SEG" + i);
        for (int j = 0; j < 20; j++) {
          s.addElement(new HL7DefinitionElement("" + j + j + j, "" + j));
        }
        _instance.addSegment(s);
      }
    }
    return _instance;
  }
}

class HL7DefinitionSegmentModel extends Observable implements javax.swing.ComboBoxModel<HL7DefinitionSegment> {
  private HL7DefinitionModel model;
  private HL7DefinitionSegment selectedItem;

  public HL7DefinitionSegmentModel() {
    this.model = HL7DefinitionModel.getInstance();
    this.selectedItem = getElementAt(0);
  }

  public void addListDataListener(ListDataListener l) {}
  public void removeListDataListener(ListDataListener l) {}

  @Override
  public HL7DefinitionSegment getElementAt(int index) {
    return model.getSegments().get(index);
  }

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

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

  @Override
  public void setSelectedItem(Object anItem) {
    this.selectedItem = (HL7DefinitionSegment) anItem;
    setChanged();
    notifyObservers();
  }
}

class HL7DefinitionElementModel extends Observable implements javax.swing.ComboBoxModel<HL7DefinitionElement>, Observer {
  private HL7DefinitionSegment segment;
  private HL7DefinitionElement selectedItem;

  public HL7DefinitionElementModel() {
    this.segment = null;
  }

  @Override
  public void update(Observable o, Object arg1) {
    if (o instanceof HL7DefinitionSegmentModel) {
      segment = (HL7DefinitionSegment) ((HL7DefinitionSegmentModel) o).getSelectedItem();
      setSelectedItem(getElementAt(0));
    }
  }

  public void addListDataListener(ListDataListener l) {}
  public void removeListDataListener(ListDataListener l) {}

  @Override
  public HL7DefinitionElement getElementAt(final int index) {
      return segment.getElements().get(index);
  }

  @Override
  public int getSize() {
    return (segment != null ? segment.getElements().size() : 0);
  }

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

  @Override
  public void setSelectedItem(Object anItem) {
    this.selectedItem = (HL7DefinitionElement) anItem;
    setChanged();
    notifyObservers();
  }
}

To reproduce the error :
- Launch the application
- Click on second combobox
- Click on the first, select option 2
- Click on second combobox
- Notice how the list is empty

if you do :
- Launch the application
- Click on the first combobox, select option 2
- Click on second combobox
- Notice how the list is populated

EDIT : Here is something else that I've found : The popup menu is only blank if there is more elements in it than in the initial "fill". That is, if the combobox is filled with 20 elements in the first pick, on the next picks the popup menu will be blank if the number of elements is 21 or more, displayed if the number of elements is 20 or less.

madgangmixers
  • 150
  • 1
  • 16
  • I quite disagree with SSCCE, you can to generating Items from loop add("Item" +(i));, then I'll be sure that could be works, are those arrays generated on fly, or hardcoded (loaded before usage) as local variables – mKorbel Apr 09 '13 at 11:59
  • They're compiled from a flat txt file, through a LL-parser – madgangmixers Apr 09 '13 at 12:05
  • be sure, to check that if all events are done on EDT, simple to wrap all code lines where you add model to view into invokeLater – mKorbel Apr 09 '13 at 12:10
  • I just want to add that the data is loaded at the start of application and does not changes during execution. The only changes happens in the GUI (the comboboxes) and in the Comboboxes's model (internal state to know where to query the data). I'll try to make a SSCCE. – madgangmixers Apr 09 '13 at 12:19
  • 1
    not, aaaaach I see that now, quite common issue, change that by using MutableComboBoxModel instead of DeafultComboBoxModel, mutable is designated to react of periodical changes with proper notifiers from model_to_view, dot :-) – mKorbel Apr 09 '13 at 12:29
  • It didn't work. The new methods of MutableComboboxModel are never called. – madgangmixers Apr 09 '13 at 13:20
  • 1
    your combomodel implementation is invalid: it _must_ notify its (listdata) listeners on changes - empty addXXListener trivially make it violate its contract. Unrelated: [don't use setXXSize, ever](http://stackoverflow.com/a/7229519/203657) – kleopatra Apr 10 '13 at 18:20
  • I extended HL7DefinitionElementModel from AbstractListModel, yet the problem subsist, i'm not quite sure how this ListDataListener works but can't find any non trivial exemple, do you know of any resource i could use ? – madgangmixers Apr 11 '13 at 10:46
  • kleopatra, your suggestion pointed me in the right way : I extended HL7DefinitionElementModel from AbstractListModel as I said in previous comment, and put "fireContentsChanged(this, 0, getSize());" in the update() method. It now works. If you want to add an answer, i'll mark is as correct answer. Thank you very much ! – madgangmixers Apr 11 '13 at 15:51

0 Answers0