swing application opens and closes down immediately when invoked with assertJ command (TestNG + assertJ Swing) - swing

Trying assertJ-swing for UI testing of java-swing based application.
Situation: Sample application in JavaApp.java (with Main class) works fine when it is invoked from this java file (running it as Java application). But when it is invoked from TestNG with assertj-swing's command:
application(JavaApp.class).start();
the application opens up but closes down immediately.
Question: Is it the only approach? Please suggest resolution or any other approach to launch the application and keep it open so further operations can be performed.
JavaApp:
package jdialogdemo;
import java.awt.*;
import java.awt.event.*;
import javax.lang.model.util.SimpleElementVisitor6;
import javax.swing.*;
public class JavaApp {
public static void main(String[] args) throws Exception {
final JFrame frame = new JFrame("JDialog Demo");
final JButton btnLogin = new JButton("Click to login");
btnLogin.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e) {
LoginDialog loginDlg = new LoginDialog(frame);
loginDlg.setVisible(true);
// if logon successfully
if(loginDlg.isSucceeded()){
btnLogin.setText("Hi " + loginDlg.getUsername() + "!");
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(700, 200);
frame.setLocationRelativeTo(null);
frame.setLayout(new FlowLayout());
frame.getContentPane().add(btnLogin);
frame.setVisible(true);
}
}

AssertJ-Swing verifies that you only do access swing components on the EDT (as required because Swing is not thread safe, see Concurrency in Swing - Initial threads).
Your main method violates this (by constructing the UI from the main thread) and therefore AssertJ-Swing fails with an exception.
To correct the problem your main method should look like this:
public class JavaApp {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(() -> {
final JFrame frame = new JFrame("JDialog Demo");
final JButton btnLogin = new JButton("Click to login");
//...
});
}
}

Related

JavaFX application thread exits during JUnit test

I'm testing a JavaFX application with JUnit, In most cases I use the #Rule approach from Basic JUnit test for JavaFX 8. However there are a couple of situations where this approach does not work, so I setup the JavaFX platform manually, and call Platform.runLater() where necessary.
What appears to be happening is that at some point the JavaFX application thread is disappearing, this means subsequent tests lockup as the Platform.runLater() calls never return - that's why I've added timeouts in example. I've proved this with calls to Thread.getAllStackTraces()
Yes I'm aware of JemmyFX, and I'll likely move to it soon, but I'd still like to understand what's going on here...
Code
public class JavaFxThreadJUnit {
private static boolean setup;
private Stage stage;
#Before
public void before() throws Exception {
setupJavaFX();
Platform.setImplicitExit(true);
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
stage = new Stage();
stage.show();
latch.countDown();
});
latch.await(5, TimeUnit.SECONDS);
}
#After
public void after() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
stage.hide();
latch.countDown();
});
latch.await(5, TimeUnit.SECONDS);
}
#Test
public void foo() throws Exception {
// test stuff...
System.out.println("foo test: "
+ Thread.getAllStackTraces().keySet().stream().map(Thread::getName).collect(Collectors.toList()));
}
#Test
public void bar() throws Exception {
// test stuff...
System.out.println("bar test: "
+ Thread.getAllStackTraces().keySet().stream().map(Thread::getName).collect(Collectors.toList()));
}
// https://gist.github.com/andytill/3835914
public static void setupJavaFX() throws InterruptedException {
if (setup) {
return;
}
long timeMillis = System.currentTimeMillis();
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// initializes JavaFX environment
new JFXPanel();
latch.countDown();
}
});
System.out.println("javafx initialising...");
latch.await();
System.out.println("javafx is initialised in " + (System.currentTimeMillis() - timeMillis) + "ms");
setup = true;
}
}
Output... JavaFX Application Thread was there, then it's gone...
javafx initialising...
javafx is initialised in 327ms
bar test: [Thread-3, ReaderThread, AWT-Shutdown, AWT-Windows, Thread-2, Finalizer, JavaFX Application Thread, Signal Dispatcher, Java2D Disposer, AWT-EventQueue-0, main, Attach Listener, Reference Handler, QuantumRenderer-0]
foo test: [Thread-3, ReaderThread, Java2D Disposer, AWT-Windows, Thread-2, main, Finalizer, Attach Listener, Reference Handler, Signal Dispatcher]
Looks like the stage closing is triggering an "implicit exit". I'd still be interested to know why this doesn't also affect tests using the #Rule approach...
Workaround:
Platform.setImplicitExit(false)

How to listen for close in a JPanel

I am working with some strange legacy code. They have a custom object which implements a JPanel. This JPanel object is a secondary popup screen within the main application. The issue I'm having is to detect when the secondary popup screen is closed.
I tried to implement a WindowListener for the class, but when I try to add it, there is no JFrame associated with this object. I am assuming this is because they are using a custom object and it is an embedded popup screen.
I tried to retrieve a JFrame using:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
which fails on a NullPointerException. I have no idea why it's so difficult to detect the right hand corner "x" close button on this page! I should mention that they were able to add Mouse and Key Listeners to the table which is embedded within the JPanel. But the outside listener for the entire window is causing me troubles.
(Please bear with me, this is my first stackoverflow post and I am new to Swing.)
Thanks so very much!!
Try to call getParent() for that strange panel. It should return the parent GUI component. If this is still not your frame but some intermediate panel instead, call getParent() on it as well. The top level component returns null.
Component p = strangePanel;
while ( p != null && ! (p instanceof Window))
p = p.getParent();
((Window) p ).addWindowListener(..);
Cannot understand why you are getting "NullPointerException" at:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
In two cases this can happen:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(null);
In your case, this is not possible as you have used this as a parameter.
Second, are you doing some other operations in above code line, like:
JFrame parentFrame = ((JFrame) SwingUtilities.getWindowAncestor(this)).someOperation();
In this case, if your this object represent the top window then you are supposed to get "NullPointerException" because ancestor of top parent is returned as "null". In other cases, I suspect you will get this exception.
Can you post a block of code where you are getting exception.
For this answer I'm making a minor assumption that the Nullpointer is not being thrown at the line that you mentioned, but rather when you attempt to add the WindowListener to the parentFrame. This is most likely because you're calling
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
before the JPanel has been added to the JFrame hierarchy.
Here's a rought code sample on how you could work around this. The thought it to wait for the panel to be notified that it has been attached to the JFrame somewhere in its hierarchy.
package test;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class HierarchyTest extends JPanel {
protected static void loadApp() {
HierarchyTest test = new HierarchyTest();
JFrame frame = new JFrame();
frame.add(test);
frame.setSize(200, 200);
frame.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
loadApp();
}
});
}
public HierarchyTest() {
this.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
// This can be optimized by checking the right flags, but I leave that up to you to look into
boolean connected = setupListenersWhenConnected();
if (connected) {
HierarchyTest.this.removeHierarchyListener(this);
}
}
});
}
protected boolean setupListenersWhenConnected() {
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
if (parentFrame == null) {
return false;
}
parentFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
// Implementation here
System.out.println("This window is closing!");
}
});
return true;
}
}

NullPointerException at java.awt.Window.access$700(Window.java:132) while painting JPanel

I'm trying to paint component inside paint(Graphics) method of JPanel.
The following code snippet works just fine, a JButton is painted nicely in my JPanel:
#Override
public void paint(Graphics g) {
super.paint(g);
JButton btn = new JButton("hello");
Dimension dim = btn.getPreferredSize();
btn.setSize(dim.width, dim.height);
btn.paint(g); // paint the button
}
The code snippet works perfectly also for other components (JLabel, JTree, ...) except JPanel.
The following code will cause very strange NullPointerException at java.awt.Window.access$700(Window.java:132).
#Override
public void paint(Graphics g) {
super.paint(g);
JPanel panel = new JPanel();
panel.setSize(10, 10);
panel.paint(g); // paint the panel
}
Here the full stacktrace:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at java.awt.Window.access$700(Window.java:132)
at java.awt.Window$1.isOpaque(Window.java:3458)
at javax.swing.RepaintManager.getVolatileOffscreenBuffer(RepaintManager.java:983)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1395)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:294)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at test.paintcontainer.TestPaintContainerMain$TestContentPane.paint(TestPaintContainerMain.java:48)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5131)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
at java.awt.Container.paint(Container.java:1780)
at java.awt.Window.paint(Window.java:3375)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:602)
at java.awt.EventQueue$1.run(EventQueue.java:600)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Any idea how to solve this problem? I need to paint JPanel inside paint(Graphics) method.
I wrote a simple test application which you can copy-paste to reproduce the aforementioned exception:
package test.paintcontainer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class TestPaintContainerMain extends JFrame {
public static void main(String[] args) {
TestPaintContainerMain test = new TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setBounds(0, 0, 300, 200);
test.setContentPane(new TestContentPane());
test.setVisible(true);
}
static class TestContentPane extends JPanel {
JRadioButton paintButtonCheck;
JRadioButton paintPanelCheck;
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
createButton().paint(g);
} else {
createPanel().paint(g);
}
}
private JButton createButton() {
JButton button = new JButton("button");
button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
return button;
}
private JPanel createPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.GREEN);
panel.add(createButton());
panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
return panel;
}
private JRadioButton createRadioButton(String title, boolean selected) {
JRadioButton radio = new JRadioButton(title, selected);
radio.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TestContentPane.this.repaint();
}
});
return radio;
}
}
}
This is most likely not a bug in Swing, but more of a problem because you are trying to paint a component which has not yet been realized, meaning it has no active graphic context. You can realize a component by adding it to already realized component like your JFrame - which itself gets realized by setVisible(true).
Also one should probably never call JComponent.paint(Graphics) manually, because this is the job of Swing (more precisely the Event Dispatcher Thread) - it even says so in the documentation of the paint method:
Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.
What you can call is the method printAll(Graphics g), which paints the component and all its subcomponents. Also in Swing one should also not override paint but paintComponent.
So here is a test code:
JButton button = createButton();
JPanel panel = createPanel();
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
//Hack, just prove something (realize both components)
add(panel);
add(button);
}
...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
button.paintAll(g);
} else {
panel.paintAll(g);
}
g.translate(-100, -100);
}
This should work (although you will obviously have two components on the screen you don't want). Also note "reset" the graphics object, because it will still be used afterwards by Swing.
So this is the theory, but it's not yet an actual solution.
My solution to your problem is: "Don't do it like this"!
Components are not like images, in the sense that they don't look the same everywhere. The output of the paintAll call will be different, depending on how (or where) the components were realized.
So one suggestion is to show actual components. Create your tooltip box, add your panel and your button and let them draw themselves. You can even subclass these components and override their paintComponent() methods, add transparency and all. It will require some work, but Swing was never known to be easy.
I just found a solution.
The only modification of a sample code from my question is that called panel.setDoubleBuffered(false) on JPanel I was trying to paint.
However, I would still consider the exeption to be a Swing bug. If double buffering should be turned off by design you shouldn't get NullPointerException but some other, more meaningful exception which explains the condition.
Here is a fixed sample application:
package test.paintcontainer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class TestPaintContainerMain extends JFrame {
public static void main(String[] args) {
TestPaintContainerMain test = new TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setBounds(0, 0, 300, 200);
test.setContentPane(new TestContentPane());
test.setVisible(true);
}
static class TestContentPane extends JPanel {
JRadioButton paintButtonCheck;
JRadioButton paintPanelCheck;
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", false);
paintPanelCheck = createRadioButton("paint panel", true);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
createButton().paint(g);
} else {
createPanel().paint(g);
}
}
private JButton createButton() {
JButton button = new JButton("button");
button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
return button;
}
private JPanel createPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.GREEN);
panel.add(createButton());
// --------------------------------
panel.setDoubleBuffered(false); // <-- TURN OFF DOUBLE BUFFERING
// --------------------------------
panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
return panel;
}
private JRadioButton createRadioButton(String title, boolean selected) {
JRadioButton radio = new JRadioButton(title, selected);
radio.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TestContentPane.this.repaint();
}
});
return radio;
}
}
}

To invokeLater() or Not

I was writing an example to showcase the usage of SwingUtilities.invokeLater(). I came to realize this simple example (code included) I have written didn't warranty the use of invokeLater(). I do encounter there were times I needed to use invokeLater() but I forgotten where and when I used. I also understand that in a non EDP thread I should use invokeLater() but in my case I don't seem to need that and it worked fine. I was hoping anyone advise me why I don't need to use invokeLater() in this piece of code. I hope there is no bug in my showcase code.
Btw, I am using JDK 1.6 / 1.7 in Linux and Windows.
Thanks.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class HelloButton {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setLayout(new FlowLayout());
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JSplitPane pane = new JSplitPane();
f.add(pane);
final JLabel clickMessage = new JLabel("No Message at " + System.currentTimeMillis());
pane.setLeftComponent(clickMessage);
JButton clickMe = new JButton("Click me");
clickMe.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
threadedIntensiveProcess(clickMessage);
// intensiveProcess(clickMessage);
}
});
pane.setRightComponent(clickMe);
f.pack();
f.setVisible(true);
}
static private void threadedIntensiveProcess(final JLabel label)
{
new Thread(new Runnable() {
#Override
public void run() {
System.out.println("UI Thread : " + SwingUtilities.isEventDispatchThread());
intensiveProcess(label);
}
}).start();
}
static private void intensiveProcess(JLabel label)
{
label.setText("was clicked at " + System.currentTimeMillis());
for (int i = 0; i < 3; i++)
{
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
label.setText("was updated at " + System.currentTimeMillis());
}
System.out.println(label.getText());
}
}
There are two situations here where you should use invokeLater.
The creation of the UI.
The update of the JLabel.
The whole creation of the UI should be done in the EDT. In ancient Swing times it was said that you could do any creation until show or setVisible(true) in the main thread as no threading issues could arise up to that point. As this is and was doubtful this is now discouraged; it may work -obviously, as in your case- but there is no guarantee.
You are lucky that setText in JLabel behaves well in this context. As it is not guaranteed by the API that the method is threadsafe you should call this method in the EDT like any other call to Swing methods.
So, in conclusion: You have a simple example that seems to work - because it is simple and you are lucky. Don't rely on such "tests" but on the documentation. If your showcase involves the presentation of the code then you have to move the calls to the EDT for not being wrong.
I don't understand why your code shouldn't "warrant" the use of invokeLater.

TreeModelListener not responding to changes in TreeModel which it subscribes to

I'm having some bother understanding why I cannot get a TreeModelChanged listener to respond to changes in the model which it subscribes to.
I have managed to reproduce the problem in a small example.
The SysOut message does not print to the console whenever a new node is added to the tree.
I intend to replace the SysOut message with some commands to redraw the tree. At the moment I am using a SysOut message just to prove that the listener is not being fired.
Am I missing something fundamental?
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class TreeTest {
private JTree t;
private DefaultTreeModel m ;
public static void main(String[] args) {
new TreeTest();
}
public TreeTest() {
//Draw Frame & Panel - set dimensions
JFrame f = new JFrame();
f.setSize(new Dimension(800,600));
JPanel p = new JPanel();
p.setSize(new Dimension(800,600));
//Create a Tree Model. Give it a String at the root.
m = new DefaultTreeModel(new DefaultMutableTreeNode("Root"));
//Create a tree and add the Model to it
t = new JTree();
t.setModel(m);
//Try a Tree Model Listener
m.addTreeModelListener(new TreeModelListener() {
private void doSomething() {
//Should fire whenever a node is added to the model
System.out.println("Responding to TreeModelListener");
}
#Override
public void treeStructureChanged(TreeModelEvent e) {
doSomething();
}
#Override
public void treeNodesRemoved(TreeModelEvent e) {
doSomething();
}
#Override
public void treeNodesInserted(TreeModelEvent e) {
doSomething();
}
#Override
public void treeNodesChanged(TreeModelEvent e) {
doSomething();
}
});
//Add listener to a button which adds nodes to the tree when clicked
JButton addNode = new JButton("Add node");
addNode.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New Node");
DefaultMutableTreeNode root = (DefaultMutableTreeNode) m.getRoot();
root.add(newNode);
}
});
JScrollPane s = new JScrollPane(t);
p.add(s);
p.add(addNode);
p.setVisible(true);
f.add(p);
f.setVisible(true);
}
}
that's because the model doesn't know about the addition, it happens under its feet. Use the methods on DefaultTreeModel to do the insertion:
model.insertNodeInto(newNode, root, root.getChildCount())
Edit
a TreeNode is just a (more or less) dumb data structure. As you can see in the api, it's not an Observable, so there is no way for the model which uses that data structure to detect if anything changed on the node. To make it aware of the change, you have to do one of two things
use the node manipulation methods of the model
update the node and notify the model manually (calling nodesWereInserted)
The first is the preferable way (keeps control where it belongs), the second might be needed in more complex contexts (though I would strongly recommend to never do it, that's why SwingX DefaultTreeTableModel doesn't have them exposed :)