Multiple ITEM_STATE_CHANGED events from jcheckbox in jtree
===

Somebody, explain me, please, how it works. Here is the code:

	package checkboxtree;
	
	import java.awt.BorderLayout;
	import java.awt.Color;
	import java.awt.Component;
	import java.awt.Font;
	import java.awt.event.ItemEvent;
	import java.awt.event.ItemListener;
	import java.awt.event.MouseEvent;
	import java.util.EventObject;
	import java.util.Vector;
	
	import javax.swing.AbstractCellEditor;
	import javax.swing.JCheckBox;
	import javax.swing.JFrame;
	import javax.swing.JScrollPane;
	import javax.swing.JTree;
	import javax.swing.UIManager;
	import javax.swing.event.ChangeEvent;
	import javax.swing.tree.DefaultMutableTreeNode;
	import javax.swing.tree.DefaultTreeCellRenderer;
	import javax.swing.tree.TreeCellEditor;
	import javax.swing.tree.TreeCellRenderer;
	import javax.swing.tree.TreePath;
	
	public class CheckBoxNodeTreeSample {
	  public static void main(String args[]) {
	    JFrame frame = new JFrame("CheckBox Tree");
	
	    CheckBoxNode accessibilityOptions[] = {
	        new CheckBoxNode(
	            "Move system caret with focus/selection changes", false),
	        new CheckBoxNode("Always expand alt text for images", true) };
	    CheckBoxNode browsingOptions[] = {
	        new CheckBoxNode("Notify when downloads complete", true),
	        new CheckBoxNode("Disable script debugging", true),
	        new CheckBoxNode("Use AutoComplete", true),
	        new CheckBoxNode("Browse in a new process", false) };
	    Vector accessVector = new NamedVector("Accessibility",
	        accessibilityOptions);
	    Vector browseVector = new NamedVector("Browsing", browsingOptions);
	    Object rootNodes[] = { accessVector, browseVector };
	    Vector rootVector = new NamedVector("Root", rootNodes);
	    JTree tree = new JTree(rootVector);
	
	    CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
	    tree.setCellRenderer(renderer);
	
	    tree.setCellEditor(new CheckBoxNodeEditor(tree));
	    tree.setEditable(true);
	
	    JScrollPane scrollPane = new JScrollPane(tree);
	    frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
	    frame.setSize(300, 150);
	    frame.setVisible(true);
	  }
	}
	
	class CheckBoxNodeRenderer implements TreeCellRenderer {
	  private JCheckBox leafRenderer = new JCheckBox();
	
	  private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
	
	  Color selectionBorderColor, selectionForeground, selectionBackground,
	      textForeground, textBackground;
	
	  protected JCheckBox getLeafRenderer() {
	    return leafRenderer;
	  }
	
	  public CheckBoxNodeRenderer() {
	    Font fontValue;
	    fontValue = UIManager.getFont("Tree.font");
	    if (fontValue != null) {
	      leafRenderer.setFont(fontValue);
	    }
	    Boolean booleanValue = (Boolean) UIManager
	        .get("Tree.drawsFocusBorderAroundIcon");
	    leafRenderer.setFocusPainted((booleanValue != null)
	        && (booleanValue.booleanValue()));
	
	    selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
	    selectionForeground = UIManager.getColor("Tree.selectionForeground");
	    selectionBackground = UIManager.getColor("Tree.selectionBackground");
	    textForeground = UIManager.getColor("Tree.textForeground");
	    textBackground = UIManager.getColor("Tree.textBackground");
	  }
	
	  public Component getTreeCellRendererComponent(JTree tree, Object value,
	      boolean selected, boolean expanded, boolean leaf, int row,
	      boolean hasFocus) {
	
	    Component returnValue;
	    if (leaf) {
	
	      String stringValue = tree.convertValueToText(value, selected,
	          expanded, leaf, row, false);
	      leafRenderer.setText(stringValue);
	      leafRenderer.setSelected(false);
	
	      leafRenderer.setEnabled(tree.isEnabled());
	
	      if (selected) {
	        leafRenderer.setForeground(selectionForeground);
	        leafRenderer.setBackground(selectionBackground);
	      } else {
	        leafRenderer.setForeground(textForeground);
	        leafRenderer.setBackground(textBackground);
	      }
	
	      if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
	        Object userObject = ((DefaultMutableTreeNode) value)
	            .getUserObject();
	        if (userObject instanceof CheckBoxNode) {
	          CheckBoxNode node = (CheckBoxNode) userObject;
	          leafRenderer.setText(node.getText());
	          System.out.println("setting state");
	          System.out.println(node.getText());
	          System.out.println(node.isSelected());
	          leafRenderer.setSelected(node.isSelected());
	        }
	      }
	      returnValue = leafRenderer;
	    } else {
	      returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree,
	          value, selected, expanded, leaf, row, hasFocus);
	    }
	    return returnValue;
	  }
	}
	
	class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
	
	  CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
	  public static int clickCount = 0;
	
	  ChangeEvent changeEvent = null;
	
	  JTree tree;
	
	  public CheckBoxNodeEditor(JTree tree) {
	    this.tree = tree;
	  }
	
	  public Object getCellEditorValue() {
		  System.out.println("getCellEditorValue method called!");
	    JCheckBox checkbox = renderer.getLeafRenderer();
	    CheckBoxNode checkBoxNode = new CheckBoxNode(checkbox.getText(),
	        checkbox.isSelected());
	    return checkBoxNode;
	  }
	
	  public boolean isCellEditable(EventObject event) {
	    
		  System.out.println("isCellEditable method called");
		  
		  boolean returnValue = false;
	    if (event instanceof MouseEvent) {
	      MouseEvent mouseEvent = (MouseEvent) event;
	      TreePath path = tree.getPathForLocation(mouseEvent.getX(),
	          mouseEvent.getY());
	      if (path != null) {
	        Object node = path.getLastPathComponent();
	        if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
	          DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
	          Object userObject = treeNode.getUserObject();
	          returnValue = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));
	        }
	      }
	    }
	    return returnValue;
	  }
	
	  public Component getTreeCellEditorComponent(JTree tree, Object value,
	      boolean selected, boolean expanded, boolean leaf, int row) {
	
		  System.out.println("getTreeCellEditorComponent method called!");
		  
		  System.out.println(String.valueOf(clickCount++));
		  
	    Component editor = renderer.getTreeCellRendererComponent(tree, value,
	        true, expanded, leaf, row, true);
	
	    // editor always selected / focused
	    ItemListener itemListener = new ItemListener() {
	      public void itemStateChanged(ItemEvent itemEvent) {
	    	  JCheckBox item = (JCheckBox)itemEvent.getItem();
	    	  System.out.println(itemEvent.paramString());
	    	  System.out.println("item "+item.getText()+" state Changed!");
	    	  
	    	  if (stopCellEditing()) {
	    		  fireEditingStopped();
	    	  }
	      }
	    };
	    if (editor instanceof JCheckBox) {
	      ((JCheckBox) editor).addItemListener(itemListener);
	    }
	
	    return editor;
	  }
	}
	
	class CheckBoxNode {
	  String text;
	
	  boolean selected;
	
	  public CheckBoxNode(String text, boolean selected) {
	    this.text = text;
	    this.selected = selected;
	  }
	
	  public boolean isSelected() {
	    return selected;
	  }
	
	  public void setSelected(boolean newValue) {
	    selected = newValue;
	  }
	
	  public String getText() {
	    return text;
	  }
	
	  public void setText(String newValue) {
	    text = newValue;
	  }
	
	  public String toString() {
	    return getClass().getName() + "[" + text + "/" + selected + "]";
	  }
	}
	
	class NamedVector extends Vector {
	  String name;
	
	  public NamedVector(String name) {
	    this.name = name;
	  }
	
	  public NamedVector(String name, Object elements[]) {
	    this.name = name;
	    for (int i = 0, n = elements.length; i < n; i++) {
	      add(elements[i]);
	    }
	  }
	
	  public String toString() {
	    return "[" + name + "]";
	  }
	}

and here is the output:

	isCellEditable method called
	getTreeCellEditorComponent method called!
	0
	setting state
	Use AutoComplete
	true
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=DESELECTED
	item Use AutoComplete state Changed!
	getCellEditorValue method called!
	setting state
	Use AutoComplete
	false
	setting state
	Use AutoComplete
	false
	setting state
	Use AutoComplete
	false

	isCellEditable method called
	getTreeCellEditorComponent method called!
	1
	setting state
	Use AutoComplete
	false
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=SELECTED
	item Use AutoComplete state Changed!
	getCellEditorValue method called!
	setting state
	Use AutoComplete
	true
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=SELECTED
	item Use AutoComplete state Changed!
	setting state
	Use AutoComplete
	true
	setting state
	Use AutoComplete
	true

	isCellEditable method called
	getTreeCellEditorComponent method called!
	2
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=checkboxtree.CheckBoxNode[Use AutoComplete/true]],stateChange=DESELECTED
	item checkboxtree.CheckBoxNode[Use AutoComplete/true] state Changed!
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=checkboxtree.CheckBoxNode[Use AutoComplete/true]],stateChange=DESELECTED
	item checkboxtree.CheckBoxNode[Use AutoComplete/true] state Changed!
	setting state
	Use AutoComplete
	true
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=SELECTED
	item Use AutoComplete state Changed!
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=SELECTED
	item Use AutoComplete state Changed!
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=DESELECTED
	item Use AutoComplete state Changed!
	getCellEditorValue method called!
	setting state
	Use AutoComplete
	false
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=DESELECTED
	item Use AutoComplete state Changed!
	ITEM_STATE_CHANGED,item=javax.swing.JCheckBox[,40,84,129x24,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@b179c3,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=2,bottom=2,right=2],paintBorder=false,paintFocus=false,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Use AutoComplete],stateChange=DESELECTED
	item Use AutoComplete state Changed!
	setting state
	Use AutoComplete
	false
	setting state
	Use AutoComplete
	false

I can't understand why method ***getTreeCellRendererComponent*** is called three times after method ***getCellEditorValue***. 

And why ***JCheckbox*** generating so many **ITEM_STATE_CHANGED** events when I click on it more then one.