// Import the W3C DOM clas ses
import org.w3c.dom.*;

// We are going to use JAXP's classes for DOM I/O
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;

// Import other Java classes
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;

/** 
Displays XML in a JTree
*/

public class XML2JTree extends JPanel
{
   private  JTree    jTree;
   private static    JFrame   frame;

   public static final int FRAME_WIDTH = 440;
   public static final int FRAME_HEIGHT = 280;

   public XML2JTree( Node root, boolean showDetails )
   {
      // Take a DOM and convert it to a Tree model for the JTree
      DefaultMutableTreeNode top = createTreeNode(root, showDetails );
      DefaultTreeModel dtModel = new DefaultTreeModel(top);

      // Create our JTree
      jTree = new JTree(dtModel);

      // We have no tree listener but this looks more natrual
      jTree.getSelectionModel().setSelectionMode(
         TreeSelectionModel.SINGLE_TREE_SELECTION);
      jTree.setShowsRootHandles(true);

      // If this were editable it would result in too much code
      // for this demo.
      jTree.setEditable(false);

      // Create a new JScrollPane but override one of the methods.
      JScrollPane jScroll = new JScrollPane()
      {
         // This keeps the scrollpane a reasonable size
         public Dimension getPreferredSize()
         {
            return new Dimension( FRAME_WIDTH-20, FRAME_HEIGHT-40 );
         }
      };

      // Wrap the JTree in a JScroll so that we can scroll it in the JSplitPane.
      jScroll.getViewport().add(jTree);

      JPanel panel = new JPanel();
      panel.setLayout(new BorderLayout());   
      panel.add("Center", jScroll);
      add("Center", panel);
   }

   /**

   This takes a DOM Node and recurses through the children until each one is
   added to a DefaultMutableTreeNode. This can then be used by the JTree as a
   tree model. The second parameter can be used to provide more visual detail
   for debugging.

   */
   protected DefaultMutableTreeNode createTreeNode( Node root, boolean showDetails )
   {
      DefaultMutableTreeNode dmtNode = null;

      String type = getNodeType(root);
      String name = root.getNodeName();
      String value = root.getNodeValue();

      if( showDetails )
      {
         dmtNode = new DefaultMutableTreeNode("["+type+"] --> "+name+" = "+value);
      }
      else
      {
         // Special case for TEXT_NODE, others are similar but not catered for here.
         dmtNode = new DefaultMutableTreeNode(
            root.getNodeType() == Node.TEXT_NODE ? value : name );
      }

      // Display the attributes if there are any
      NamedNodeMap attribs = root.getAttributes();
      if(attribs != null && showDetails )
      {
         for( int i = 0; i < attribs.getLength(); i++ )
         {
            Node attNode = attribs.item(i);
            String attName = attNode.getNodeName().trim();
            String attValue = attNode.getNodeValue().trim();

            if(attValue != null)
            {
               if (attValue.length() > 0)
               {
                  dmtNode.add(new DefaultMutableTreeNode(
                     "[Attribute] --> "+attName+"=\""+attValue+"\"") );
               }
            }
         }
      }

      // If there are any children and they are non-null then recurse...
      if(root.hasChildNodes())
      {
         NodeList childNodes = root.getChildNodes();
         if(childNodes != null)
         {
            for (int k=0; k<childNodes.getLength(); k++)
            {
               Node nd = childNodes.item(k);
               if( nd != null )
               {
                  // A special case could be made for each Node type.
                  if( nd.getNodeType() == Node.ELEMENT_NODE )
                  {
                     dmtNode.add(createTreeNode(nd, showDetails));
                  }

                  // This is the default
                  String data = nd.getNodeValue();
                  if(data != null)
                  {
                     data = data.trim();
                     if(!data.equals("\n") && !data.equals("\r\n") &&
                        data.length() > 0)
                     {
                        dmtNode.add(createTreeNode(nd, showDetails));
                     }
                  }
               }
            }
         }
      }
      return dmtNode;
   }

   /**

   This simple method returns a displayable string given a NodeType

   */
   public String getNodeType( Node node )
   {
      String type;

      switch( node.getNodeType() )
      {
         case Node.ELEMENT_NODE:
         {
            type = "Element";
            break;
         }
         case Node.ATTRIBUTE_NODE:
         {
            type = "Attribute";
            break;
         }
         case Node.TEXT_NODE:
         {
            type = "Text";
            break;
         }
         case Node.CDATA_SECTION_NODE:
         {
            type = "CData section";
            break;
         }
         case Node.ENTITY_REFERENCE_NODE:
         {
            type = "Entity reference";
            break;
         }
         case Node.ENTITY_NODE:
         {
            type = "Entity";
            break;
         }
         case Node.PROCESSING_INSTRUCTION_NODE:
         {
            type = "Processing instruction";
            break;
         }
         case Node.COMMENT_NODE:
         {
            type = "Comment";
            break;
         }
         case Node.DOCUMENT_NODE:
         {
            type = "Document";
            break;
         }
         case Node.DOCUMENT_TYPE_NODE:
         {
            type = "Document type";
            break;
         }
         case Node.DOCUMENT_FRAGMENT_NODE:
         {
            type = "Document fragment";
            break;
         }
         case Node.NOTATION_NODE:
         {
            type = "Notation";
            break;
         }
         default:
         {
            type = "Unknown, contact Sun!";
            break;
         }
      }
      return type;
   }

   /**

   A common point of exit...

   */

   private static void exit()
   {
      System.out.println("Graceful exit");
      System.exit(0);
   }

   /**

   This main is simply to test this class

   */

   public static void main( String[] args )
   {
      Document doc = null;

      // Just check we have the right parameters first.
      if( args.length < 1 )
      {
         System.out.println( "Usage:\tXML2JTree filename.xml" );
         return;
      }
      String filename = args[0];

      boolean showDetails = false;
      if( args.length == 2 )
      {
         // If anything's there override the default
         showDetails = Boolean.valueOf(args[1]).booleanValue();
      }

      // Create a frame to "hold" our class
      frame = new JFrame("XML to JTree");

      Toolkit toolkit = Toolkit.getDefaultToolkit();
      Dimension dim = toolkit.getScreenSize();
      int screenHeight = dim.height;
      int screenWidth = dim.width;

      // This should display a WIDTH x HEIGHT sized Frame in the middle of the screen
      frame.setBounds( (screenWidth-FRAME_WIDTH)/2,
         (screenHeight-FRAME_HEIGHT)/2, FRAME_WIDTH, FRAME_HEIGHT );

      frame.setBackground(Color.lightGray);
      frame.getContentPane().setLayout(new BorderLayout());

      // Give our frame an icon when it's minimized.
      frame.setIconImage(toolkit.getImage("Wrox.gif"));

      // Add a WindowListener so that we can close the window
      WindowListener wndCloser = new WindowAdapter()
      {
         public void windowClosing(WindowEvent e)
         {
            exit();
         }
      };
      frame.addWindowListener(wndCloser);

      try
      {
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         dbf.setValidating(false);  // Not important fro this demo

         DocumentBuilder db = dbf.newDocumentBuilder();
         doc = db.parse(filename);
      }
      catch( FileNotFoundException fnfEx )
      {
         // Display a "nice" warning message if the file isn't there.
         JOptionPane.showMessageDialog(frame, filename+" was not found",
            "Warning", JOptionPane.WARNING_MESSAGE);

         System.out.println();
         exit();
      }
      catch( Exception ex )
      {
         JOptionPane.showMessageDialog(frame, ex.getMessage(), "Exception",
                                       JOptionPane.WARNING_MESSAGE);

         ex.printStackTrace();
         exit();
      }

      Node root = (Node)doc.getDocumentElement();

      frame.getContentPane().add(new XML2JTree( root, showDetails ),
         BorderLayout.CENTER);

      frame.validate();
      frame.setVisible(true);
   }
}
