Handles in JTree

I've made an application connecting JTree nodes with JTable.
When I first click on any cell in JTable and then on an JTree's node handle icon, I get an Exception: "java.lang.NullPointerException".
But, when I first click on any cell in JTable and then on an JTree's node icon, everything is OK.
What is goin' on?

It happens that when I click on the handle icon of the ancestor node, an Exception occurs:
java.lang.NullPointerException
What is goin' on?
Here is code snippet:
void jTree1_valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)jTree1.getLastSelectedPathComponent();
Object birac = node.getUserObject();
TreePath niz = (TreePath)jTree1.getLeadSelectionPath();
jTextArea1.setText("");
if (jTree1.isVisible(niz)) {
if (node.isLeaf()) {
My_Class bt = (My_Class)birac;
String tabela1 = bt.getImeTabele();
if (qds1.isOpen()) {
try {
qds1.close();
} catch (Exception ex){} } //if

Similar Messages

  • Show + image instead of handle in jtree

    I have created jtree. I have implemented my own TreeCellRendererComponent. I set the node images as per node type. My problem is, I need to get '+' sign to nodes not expanded and '-' for those expanded instead of default handles. How do I implement it?
    public Component getTreeCellRendererComponent(
                        JTree tree, Object value, boolean isSelected,
                        boolean isExpanded, boolean isLeaf, int row,
                        boolean hasFocus) {
              super.getTreeCellRendererComponent( tree, value, isSelected,
                                  isExpanded, isLeaf, row, hasFocus);
              Component compObject = this;
              if ( value instanceof EuamDefaultMutableTreeNode ) {
                   EuamDefaultMutableTreeNode node = (EuamDefaultMutableTreeNode)value;
                   setText( node.getText() );
                   setIcon(isExpanded ? node.getOpenIcon() : node.getClosedIcon() );
                   if( bIsRightImage_ && lblRightSideIcon_ != null ) {
                        lblRightSideIcon_.setIcon(node.getRightIcon());
              else {
                   setText(value.toString());
              return compObject;
    }

    Tree node handles vary depending on which L&F u r using, java defaults to "javax.swing.plaf.metal.MetalLookAndFeel" which uses the "0-" type handles. To get the '+', '-' handles you need to be using windows L&F. Try adding this code to your apps main method:
    try{
       UIManager.setLookAndFeel(
            "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    }catch (Exception e) {}Hope this helps

  • Adding listeners to handles in JTree

    Hi,
    I wanted to know can listeners be added to the handles in the topmost level in a JTree. If yes how can this be done?
    Thanks,
    Sheetul

    And there's a TreeSelectionListener that tells you when the user clicked on a node to select it. But clicking on the node handles is not the normal way to select them.

  • Multiple JTrees

    Hi everybody!
    I've got to create something very similar to a file commander - something like dos NC or linux MC. The difference is there are my objects - called "resources" - instead of files. I've created a JFrame with two separate JPanels, each of them handling separate JTree.
    The problem is when I create o DefaultMutableTreeNode with the same user object for both JTrees, the node is shown only on the first of them. When I switch the order of adding folders, the node is visible only on the latter one.
    Can anybody help me? Thanx!

    Hi Pipzeno! I've just solved problem on my own. I've found a stupid mistake in my code. The method addFolders(Vector folders) in the class representing a JTree was removing all objects from the given Vector. So, the second JTree was given an empty Vector as the parameter.
    Anyway, Merry X-mas! :))))

  • Jtree node refuses to collapse upon clicking handle; makeVisible() was used

    Hello,
    While creating a new node and inserting as a leaf in the JTree, I use tree.makeVisible(newTreePath) to expand the new node and make visible. (Using expandPath() will not expand if a leaf was added).
    However now the jtree node refuses to collapse upon clicking the node handle.
    How do I get it to not insist on staying open - be able to collapse manually?
    thanks,
    Anil

    This is the JNI forum. I don't believe your post fits.
    then
    can I correctly assume that any class that
    "implements Cloneable" will handle making either a
    "shallow" or "deep" ... or even "semi-shallow" clone,
    respective to the class context .. right?
    It probably does something. The implementor might not have implemented anything though. And you have no idea what they implemented.
    No idea about your other question.
    You might want to think carefully about why you are cloning though.

  • Jtree Handles but no children problem

    My tree uses data from a database to populate. What I want is there to be the little handle image to open and close, whether or not there are children. The reason why is, I don't want the database queried until the uses tries to expand the node. I've tried using the DefaultCellRenderer to set leaf nodes to the default closed position and open position, but i just get little folder not the expandable handles. It needs to behave as though is has children, even though it has none. Right now I have it all working with a placeholder to trick each node and when I retrieve the data it vanishes (user never sees it) but it seems like there should be a better way. Help(?) jaszzmongrel

    I hate this, but don't know of a better way.import javax.swing.*;
    import javax.swing.event.*;
    import java.awt.*;
    import javax.swing.tree.*;
    import java.util.*;
    public class Test2 extends JFrame {
      private static final Random rand = new Random();
      public Test2() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container content = getContentPane();
        DefaultMutableTreeNode mtn = new DefaultMutableTreeNode("Root");
        for (int i=0; i<5; i++) mtn.add(new MyTreeNode("Node-"+i));
        JTree jt = new JTree(mtn);
        jt.addTreeWillExpandListener(new javax.swing.event.TreeWillExpandListener() {
          public void treeWillExpand(TreeExpansionEvent tse) {
            TreeNode tn = (TreeNode)tse.getPath().getLastPathComponent();
            if (tn instanceof MyTreeNode && !((MyTreeNode)tn).isChildChecked()) {
              int r = rand.nextInt(3);
              ((MyTreeNode)tn).removeAllChildren();
              for (int i=0; i<r; i++) ((MyTreeNode)tn).add(new MyTreeNode("Child-"+i));
          public void treeWillCollapse(TreeExpansionEvent tse) {}
        content.add(new JScrollPane(jt), BorderLayout.CENTER);
        jt.setShowsRootHandles(true);
        setSize(300,300);
      public static void main(String[] args) { new Test2().setVisible(true); }
    class MyTreeNode extends DefaultMutableTreeNode {
      boolean childCheck = false;
      public MyTreeNode(Object obj) {
        super(obj);
        add(new DefaultMutableTreeNode());
      public boolean isChildChecked() { return childCheck; }
      public void add(MutableTreeNode mtn) {
        if (childCheck) {
          removeAllChildren();
          childCheck=true;
        super.add(mtn);
    }

  • Help with JTree node handles icon

    Hi
    In my application i use Jtree to display the hirearchical data.But i don't want to the node handles icon to be displayed in the screen.The root node handle icon will be not be displayed if we set jtree.setShowsRootHandles(false) .Is there any way to diable the other node handles icons too?.Please help me in overcoming the problem.
    So, i will be handling the expand/collapse operations of the node with the help of mouse click event.
    Thanks in advance.
    Regards,
    Krish.

    Hi all,
    Any ideas to overcome this problem.?
    Thanks,
    Krish

  • How to hide node handles (legs) from JTree nodes?

    Hello,
    Does anyone has an idea on how to not display JTree node handles (legs)?
    Thank you,
    Mindaugas

    You can do it with Java/Metal look and feel by doing this:
    JTree tree = new JTree(...);
    tree.setClientProperty("JTree.lineStyle", "None");
    For any other L&F, you will have to look at the UI code.
    Mitch Goldstein
    Author, Hardcore JFC

  • JTree change handles of node

    Hi!
    I am using MetalLookAndFeel in my program. I also have a JTree which by default uses Java handles (o--)
    I need to show the handles of windowsLookAndFeel (+ and -) in the tree, but without changing the MetalLookAndFeel.
    Is there a way to do this in JAVA?

    To set the icons,
    UIManager.put("Tree.collapsedIcon", icon1);
    UIManager.put("Tree.expandedIcon", icon2);Note that in order to acquire the icons from the Windows L&F itself you would need to be runnin on a Windows platform, so it may be preferable to include PNGs of them in your classpath. Up to you.

  • JTree's handle icon

    Hi,
    Can we change the icon of the handles of a JTree?
    If yes, how can we do that?
    Thanks,
    Jason

    It happens that when I click on the handle icon of the ancestor node, an Exception occurs:
    java.lang.NullPointerException
    What is goin' on?
    Here is code snippet:
    void jTree1_valueChanged(TreeSelectionEvent e) {
    DefaultMutableTreeNode node = (DefaultMutableTreeNode)jTree1.getLastSelectedPathComponent();
    Object birac = node.getUserObject();
    TreePath niz = (TreePath)jTree1.getLeadSelectionPath();
    jTextArea1.setText("");
    if (jTree1.isVisible(niz)) {
    if (node.isLeaf()) {
    My_Class bt = (My_Class)birac;
    String tabela1 = bt.getImeTabele();
    if (qds1.isOpen()) {
    try {
    qds1.close();
    } catch (Exception ex){} } //if

  • JTree handles problems

    Hi everyone!
    I have a problem with handles in my JTree. The JTree is populated dynamically via RMI and and acts on expand. When I collapse a node I erase all childs so that I wont get double entries the next time I expand that node. The problem is that the + symbol disappears when i collapse since there is no children until you expand. Is there a way to force the + symbol to be visible all the time? I've tried to override the isLeaf() method to always return false but it doesn't change the behavior. The thing is that I have to erase all childs every time you collapse since the structure migth have been changed by other users.
    Any ideas?
    Thanx in advance!!!

    When I collapse a node I erase all childs so that I wont get double >entries the next time I expand that node
    Why make a dummy node ...You stated that on collapse was when the erase was done.

  • Jtree  handling  in  jdeveloper

    hello
    i am user of oracle 10g jdeveloper using jclient/swing in my product. i am using oracle 9i as a backend.
    i would to select two attribute at a time in my jtree.
    As you might be aware of that in jdeveloper in order to build a jtree we have ->
    select a view , select a display attribute , select an accessor
    question->
    how to select two display attribute corresponding to one view .
    please do help me if you can.
    thank you

    Are you making sure to point the pages to your error page?

  • Problem with JTree.isExpanded()

    Working with a JTree, I encountered into a strange behavior - I hope someone could explain me...
    For exporting a tree I need the information whether nodes are expanded or not.
    The first strange thing is that the root always seems to be collapsed - but this I can handle somehow.
    The problem is, that a expanded node containing a leaf, is reported as collapsed - but it is not. Why?
    Here the output of a expand-checking method, which prints the state of the node and its parents.
    (the "+" indicates a expanded node, the "-" indicated a collapsed node)
    + - Abasdf
    + - EP/asdf
    + - - Prosadf
    + - - - Altasdf
    + - - - - V1sadf
    + - - - - - 059asdf
    + - - - - - - 059asdf
    + - - - - - - + 059asdf
    + - - - - - - + + this_is_a_leafWhy is the expanded node containing the leaf reported as collapsed?
    I'm using the following for checking if a node is expanded:
    view.isExpanded(node.getIndex())I also tried this - with the same wrong result:
    view.isExpanded(view.getPathForRow(node.getIndex()))Any hint is highly appretiated.

    I was not able to reproduce your problemimport javax.swing.*;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
    import javax.swing.tree.TreePath;
    import javax.swing.tree.TreeNode;
    import java.util.Random;
    import java.util.Enumeration;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    public class HamsterChipsyTest extends JPanel {
         private static final Random RANDOM = new Random(System.currentTimeMillis());
         private JTree theTree;
         private DefaultTreeModel theTreeModel;
         private DefaultMutableTreeNode theRoot;
         public HamsterChipsyTest() {
              super(new BorderLayout());
              theRoot = createRandomNode();
              addNodes(theRoot, 0, 2);
              theTreeModel = new DefaultTreeModel(theRoot);
              theTree = new JTree(theTreeModel);
              add(new JScrollPane(theTree), BorderLayout.CENTER);
              add(new JButton(new AbstractAction("dump nodes' extension state") {
                   public void actionPerformed(ActionEvent e) {
                        dumpExtensionState(theRoot);
              }), BorderLayout.NORTH);
         private void dumpExtensionState(DefaultMutableTreeNode aTreeNode) {
              TreeNode[] path = aTreeNode.getPath();
              for (int i = 0; i < path.length; i++) {
                   System.out.print('-');
              System.out.println(" " + aTreeNode.getUserObject().toString() + " " + theTree.isExpanded(new TreePath(path)));
              Enumeration children  = aTreeNode.children();
              while(children.hasMoreElements()) {
                   DefaultMutableTreeNode child = (DefaultMutableTreeNode)children.nextElement();
                   dumpExtensionState(child);
         private static void addNodes(DefaultMutableTreeNode aParent, int aCurrentDepth, int aMaxDepth) {
              if (aCurrentDepth >= aMaxDepth) return;
              int nodeCount = RANDOM.nextInt(3);
              for (int i = 0; i < nodeCount + 1; i++) {
                   DefaultMutableTreeNode child = createRandomNode();
                   aParent.add(child);
                   addNodes(child, aCurrentDepth + 1, aMaxDepth);
         private static DefaultMutableTreeNode createRandomNode() {
              char[] chars = new char[5];
              for (int i = 0; i < chars.length; i++) {
                   chars[i] = (char)('a' + RANDOM.nextInt(26));
              return new DefaultMutableTreeNode(new String(chars));
         public static void main(String[] args) {
              final JFrame frame = new JFrame(HamsterChipsyTest.class.getName());
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setContentPane(new HamsterChipsyTest());
              SwingUtilities.invokeLater(new Runnable() {
                   public void run() {
                        frame.pack();
                        frame.show();
    }

  • Comparing Two Jtree Objects

    Hi
    I had constructed two JTree objects. Now i want to find the difference between these two JTree's Node wise.I dont have any idea how to proceed further.If some one help me in this regard i would be very thankful.
    -Rosy

    The JTree component handles the 'view' side of things. The actual data it displays is held in a TreeModel. You can create your own and pass it to the JTree when you create it otherwise a DefaultTreeModel is created for you by the JTree.
    What I'm getting at is that to achieve your aim you want to compare the TreeModels rather than the JTrees.
    If you haven't created your own, you can get it from the JTree using jTree.getModel()
    Calling methods on each model and comparing the results will achieve your aim.

  • Help needed redrawing a JTree

    Hi,
    I'm writing a simple Swing program to examine the entries in a JAR file as a JTree. It is not meant to be professional grade, it's simply for my own fun.
    I have the program so that it displays a Window with a Menus. When the user chooses a JAR file, the program opens the JAR file, reads the entries in the file, and builds a DefaultTreeModel for the entries in the JAR file. Then, it builds a JTree from the DefaultTreeModel.
    The program works great, except, once the tree is created, I don't know how to force Swing to redraw the JFrame.
    Can someone please tell me what I'm doing wrong?
    The code is as follows:
    <pre>
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.tree.*;
    import java.io.*;
    import java.util.jar.*;
    import java.util.*;
    * <p>This class is used to display the contents of a JAR file.
    * The entries of the JAR file will be displayed in Swing Tree.
    * @version 1.0
    * @author Kevin Yetman
    public class JarFileTree
    * <p>This method is used to display the GUI.
    public static void main(String[] args)
    {  JFrame frame=new JarTreeFrame();
    frame.show();
    * <p>This class is used to define the javax.swing.JFrame based object that
    * will contain the tree from the JAR file.
    class JarTreeFrame extends JFrame implements ActionListener
    private JTree tree;
    private DefaultTreeModel treeModel;
    private DefaultMutableTreeNode root;
    private JScrollPane scrollPane;
    * <P>This constructor is used create and display the frame.
    public JarTreeFrame()
    {  setTitle("JAR Viewer");
    setSize(800, 600);
    addWindowListener(new WindowAdapter()
    {  public void windowClosing(WindowEvent e)
    {  System.exit(0);
    JMenuBar menuBar=setupMenuBar();
    setJMenuBar(menuBar);
    root = new DefaultMutableTreeNode("No file selected yet.");
    treeModel=new DefaultTreeModel(root);
    tree = new JTree(treeModel);
    scrollPane=new JScrollPane(tree);
    Container contentPane=getContentPane();
    contentPane.add(scrollPane);
    public void processString(String currentEntry, DefaultMutableTreeNode currentNode, DefaultTreeModel treeModel)
    if( currentEntry.length()==0 )
    {  return;
    int slashPos=currentEntry.indexOf('/');
    String currentToken="";
    String remainingPartOfCurrentEntry="";
    if( slashPos!=-1 )
    {  currentToken=currentEntry.substring(0, slashPos);
    remainingPartOfCurrentEntry=currentEntry.substring(slashPos+1, currentEntry.length());
    else
    {  currentToken=currentEntry;
    boolean childFound=false;
    for(Enumeration e=currentNode.children(); e.hasMoreElements(); )
    DefaultMutableTreeNode currentChild=(DefaultMutableTreeNode)e.nextElement();
    if( currentChild.toString().equals(currentToken) )
    processString(remainingPartOfCurrentEntry, currentChild, treeModel);
    childFound=true;
    if( !childFound )
    DefaultMutableTreeNode newChild=new DefaultMutableTreeNode(currentToken);
    treeModel.insertNodeInto(newChild, currentNode, currentNode.getChildCount());
    protected JMenuBar setupMenuBar()
    JMenuBar menuBar=new JMenuBar();
    JMenu helpMenu =new JMenu("Help");
    JMenuItem aboutItem=new JMenuItem("About");
    aboutItem.setActionCommand("HelpAbout");
    aboutItem.addActionListener(this);
    helpMenu.add(aboutItem);
    JMenu fileMenu = new JMenu("File");
    JMenuItem openItem=new JMenuItem("Open");
    openItem.setActionCommand("FileOpen");
    openItem.addActionListener(this);
    JMenuItem exitItem=new JMenuItem("Exit");
    exitItem.addActionListener( new ActionListener()
    {  public void actionPerformed(ActionEvent evt)
    { System.exit(0);
    fileMenu.add(openItem);
    fileMenu.add(exitItem);
    menuBar.add(fileMenu);
    menuBar.add(helpMenu);
    return menuBar;
    public void setupTree(String fileName)
    try
    JarFile jarFile=new JarFile(new File(fileName));
    for(Enumeration e=jarFile.entries(); e.hasMoreElements(); )
    String currentEntry=(e.nextElement().toString()).trim();
    processString(currentEntry, root, treeModel);
    DefaultMutableTreeNode lastLeaf=root.getLastLeaf();
    TreePath path=new TreePath(treeModel.getPathToRoot(lastLeaf));
    tree.makeVisible(path);
    catch(IOException e)
    {  JOptionPane.showMessageDialog(this, "ERROR: " + e.toString());
    public void actionPerformed(ActionEvent evt)
    if(evt.getActionCommand().equals("FileOpen"))
    FileOpenSaveAs fosa=new FileOpenSaveAs(this, true);
    if(fosa.approveOptionClicked())
    String fileName=fosa.getSelectedFile();
    fileName+=".jar";
    setupTree(fileName);
    Container contentPane=getContentPane();
    contentPane.removeAll();
    contentPane.add(new JScrollPane(tree));
    contentPane.repaint();
    if(evt.getActionCommand().equals("HelpAbout"))
    {  JOptionPane.showMessageDialog(this, "JAR Viewer Version 1.0\nKevin Yetman");
    // FileOpenSaveAs.java
    // Author: Kevin Yetman
    import java.awt.*;
    import java.awt.event.*;
    import java.io.*;
    import javax.swing.*;
    import javax.swing.event.*;
    // this class handles the display of the
    // File Open or File Save As box.
    public class FileOpenSaveAs {
    // the constructor takes one argument, a
    // boolean which if true opens the File Open
    // box. If false it opens the save as box.
    public FileOpenSaveAs(JFrame baseWindow, boolean fileOpenBox) {
    // initailze the result varialbe
    result=-1;
    // store the value of the fileOpenBox.
    openBox=fileOpenBox;
    // initialize the fileNameSelected string.
    fileNameSelected=new String();
    // first build a JFileChooser.
    JFileChooser fc=new JFileChooser();
    // next tell the file chooser to look in the
    // current directory.
    fc.setCurrentDirectory(new File(CURRENT_DIRECTORY));
    // if the file open box is true, display the file open box.
    // otherwise, display the file save as box.
    if( fileOpenBox ) {
    fc.setDialogTitle(OPEN_TITLE);
    displayBox(baseWindow, fc);
    else {
    fc.setDialogTitle(SAVE_AS_TITLE);
    displayBox(baseWindow, fc);
    // the constructor takes one argument, a
    // boolean which if true opens the File Open
    // box. If false it opens the save as box.
    public FileOpenSaveAs(JDialog baseWindow, boolean fileOpenBox) {
    // initailze the result varialbe
    result=-1;
    // store the value of the fileOpenBox.
    openBox=fileOpenBox;
    // initialize the fileNameSelected string.
    fileNameSelected=new String();
    // first build a JFileChooser.
    JFileChooser fc=new JFileChooser();
    // next tell the file chooser to look in the
    // current directory.
    fc.setCurrentDirectory(new File(CURRENT_DIRECTORY));
    // if the file open box is true, display the file open box.
    // otherwise, display the file save as box.
    if( fileOpenBox ) {
    fc.setDialogTitle(OPEN_TITLE);
    displayBox(baseWindow, fc);
    else {
    fc.setDialogTitle(SAVE_AS_TITLE);
    displayBox(baseWindow, fc);
    // this method displays the File Open box on the base window.
    public void displayBox(JFrame baseWindow, JFileChooser fc) {
    // display the file open box and get the result.
    result=-1;
    if( openBox==true ) {
    result=fc.showOpenDialog(baseWindow);
    else {
    result=fc.showSaveDialog(baseWindow);
    // get the file name.
    if( approveOptionClicked() ) {
    fileNameSelected=fc.getName(fc.getSelectedFile());
    else {
    fileNameSelected="";
    // this method displays the File Open box on the base window.
    public void displayBox(JDialog baseWindow, JFileChooser fc) {
    // display the file open box and get the result.
    result=-1;
    if( openBox==true ) {
    result=fc.showOpenDialog(baseWindow);
    else {
    result=fc.showSaveDialog(baseWindow);
    // get the file name.
    if( approveOptionClicked() ) {
    fileNameSelected=fc.getName(fc.getSelectedFile());
    else {
    fileNameSelected="";
    // check to see if the result is APPROVE_OPTION. If so, we
    // will return true.
    public boolean approveOptionClicked() {
    return (result == JFileChooser.APPROVE_OPTION );
    // get the file name chosen by the user.
    public String getSelectedFile() {
    return fileNameSelected;
    // private attributes for this class.
    private boolean openBox;
    private static final String CURRENT_DIRECTORY=".";
    private static final String SAVE_AS_TITLE="Save As";
    private static final String OPEN_TITLE="Open";
    private int result;
    private String fileNameSelected;
    </pre>
    Thanks!
    Kevin Yetman

    Without looking at all your code, I would suggest that you only redraw the part of the tree that changes (addition or deletion of nodes). To do this, you can call a method of DefaultTreeModel, nodeStructureChanged(TreeNode nodeThatChanged). This fires the appropriate event which in turn causes the node and only its descendents to be redrawn. Good luck.
    -Matt

Maybe you are looking for

  • Tab in Mail goes to too many wrong places in SL

    Since upgrading to Snow Leopard 10.6.1, if I tab down from the To field to get into the message area, the tab first tabs/stops at each and every box in the bar there between the Subject field and the Message field (Customize, From, SMTP, and Signatur

  • Select lists - remember values after submit not working in IE7

    I have two dynamic select lists that are populated using .csv files. On submit, a set of results is returned based on the two options that were chosen. In IE7 when the results are returned the select lists default back to the last option in the selec

  • [SOLVED] OpenGL/GLUT problem...

    Hey guys, I've recently been trying to program with OpenGL/GLUT but so far I haven't been having much luck on Arch.  I have a simple program that uses OpenGL just to test whether I've got it working or not, but I seem to be including the wrong files.

  • EBRS Upload

    Hi I have configured MT940 format for EBRS (Electronic Bank Reconciliation Statement),  when i upload text file, it is not creating a batch input session,   Can a text file be uploaed?. Or should it be only in MSG format for the batch input to be cre

  • Printing vendor return invoice - goods return using movement 122

    Dear all, Is it possible to  print vendor return invoice along with excise tax break up if we do goods return using movement type 122 ?. Regards, A.Jeyakanthan