/****************************************************************************
  $Archive: /njcl_sample/NJCLApplet/src/ContextFrame.java $
  $Revision: 2 $
  $Modtime: 3/18/99 4:30p $

  Copyright (c) 1999 Novell, Inc.  All Rights Reserved.

 THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
 USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE AGREEMENT
 ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS THIS WORK.
 PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO DEVELOPER A
 ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE CODE IN ITS
 PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS TO MARKET,
 DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF DEVELOPER'S
 PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR DEVELOPER'S
 CUSTOMERS WITH RESPECT TO THIS CODE.
 ***************************************************************************/

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.tree.*;

import javax.naming.*;
import javax.naming.directory.*;

/**
 * This class provides a graphical view control for a JNDI context.
 */

public class ContextFrame
   extends JFrame
   implements ActionListener, MouseListener
{
   private static int buttonHeight = 25;
   private static int buttonWidth = 100;
   private static int mainPaneHeight = 400;
   private static int mainPaneWidth = 500;

   // Declare controls
   private JMenuBar         mainMenuBar;
   private JMenu            fileMenu;
   private JMenuItem        exitMenuItem;
   private JMenu            viewMenu;
   private JMenuItem        lookupMenuItem;
   private JMenuItem        searchMenuItem;
   private JCheckBoxMenuItem showClassesMenuItem;
   private JMenuItem        refreshMenuItem;
   private JMenu            helpMenu;
   private JMenuItem        aboutMenuItem;
   private JButton          lookupButton;
   private JButton          attributeButton;
   private JScrollPane      treeScrollPane;
   private JTree            tree;
   private DefaultTreeModel treeModel;
   private JLabel           statusLabel;
   private JPopupMenu       popup;
   private JMenuItem        lookupPopupMenuItem;
   private JMenuItem        attributePopupMenuItem;
   private JMenuItem        schemaPopupMenuItem;
   private JMenuItem        classDefPopupMenuItem;
   private JMenuItem        renamePopupMenuItem;

   private ContextTreeNode rootNode;   
   private boolean isRoot = false;
   
   /**
    * Constructs the GUI portion of the Context frame given a root node.
    *
    * @param rootNode            The root ContextTreeNode.
    */
   public ContextFrame (
         ContextTreeNode rootNode)
   {
      super ("Context Browser");
      this.rootNode = rootNode;
      getContentPane ().setLayout (new BorderLayout ());
      setSize (mainPaneWidth, mainPaneHeight);

      // Menus
      mainMenuBar = new JMenuBar ();
      fileMenu = new JMenu ("File");
      exitMenuItem = new JMenuItem ("Exit");
      exitMenuItem.addActionListener (this);
      fileMenu.add (exitMenuItem);
      mainMenuBar.add (fileMenu);
      viewMenu = new JMenu ("View");
      lookupMenuItem = new JMenuItem ("Lookup");
      lookupMenuItem.addActionListener (this);
      viewMenu.add (lookupMenuItem);
      searchMenuItem = new JMenuItem ("Search");
      searchMenuItem.addActionListener (this);
      viewMenu.add (searchMenuItem);
      showClassesMenuItem = new JCheckBoxMenuItem (
                                    "Show classes",
                                    ContextTreeNode.showClasses);
      showClassesMenuItem.addActionListener (this);
      viewMenu.add (showClassesMenuItem);
      refreshMenuItem = new JMenuItem ("Refresh");
      refreshMenuItem.addActionListener (this);
      viewMenu.add (refreshMenuItem);
      mainMenuBar.add (viewMenu);
      helpMenu = new JMenu ("Help");
      aboutMenuItem = new JMenuItem ("About");
      aboutMenuItem.addActionListener (this);
      helpMenu.add (aboutMenuItem);
      mainMenuBar.add (helpMenu);
      setJMenuBar (mainMenuBar);

      attributePopupMenuItem = new JMenuItem ("Attribute");
      attributePopupMenuItem.addActionListener (this);
      classDefPopupMenuItem = new JMenuItem ("ClassDef");
      classDefPopupMenuItem.addActionListener (this);
      lookupPopupMenuItem = new JMenuItem ("Lookup");
      lookupPopupMenuItem.addActionListener (this);
      renamePopupMenuItem = new JMenuItem ("Rename");
      renamePopupMenuItem.addActionListener (this);
      schemaPopupMenuItem = new JMenuItem ("Schema");
      schemaPopupMenuItem.addActionListener (this);

      // Buttons
      JPanel buttonPanel = new JPanel (new FlowLayout (FlowLayout.LEFT));
      attributeButton = new JButton ("Attribute");
      attributeButton.setToolTipText ("Display attributes for an object");
      attributeButton.addActionListener (this);
      attributeButton.setSize (buttonWidth, buttonHeight);
      buttonPanel.add (attributeButton);
      lookupButton = new JButton ("Lookup");
      lookupButton.setToolTipText ("Lookup the selected object");
      lookupButton.setSize (buttonWidth, buttonHeight);
      lookupButton.addActionListener (this);
      buttonPanel.add (lookupButton);
      getContentPane ().add (buttonPanel, "North");
      
      // Tree view pane
      treeScrollPane = new JScrollPane ();
      getContentPane ().add (treeScrollPane, "Center");
      rootNode.setContextFrame (this);
      treeModel = new DefaultTreeModel (rootNode);
      tree = new JTree (treeModel);
      tree.setEditable (true);      // allow refresh
      treeScrollPane.getViewport ().add (tree);
      tree.addMouseListener (this);
      
      // Status label
      statusLabel = new JLabel ();
      statusLabel.setToolTipText ("Status");
      statusLabel.setBorder (new EtchedBorder (EtchedBorder.LOWERED));
      statusLabel.setSize (
                        mainPaneWidth,
                        buttonHeight);
      getContentPane ().add (statusLabel, "South");
      
      setDefaultCloseOperation (DISPOSE_ON_CLOSE);

   } // ContextFrame ()
   
   /**
    * Sets the status label to the given message and returns the previous
    * contents.
    *
    * @param message             The new message to display.
    * @return                    The previous contents of the status label.
    */
   public String setStatusLabel (
         String message)
   {
      String str = statusLabel.getText ();
      statusLabel.setText (message);
      return (str);
   } // setStatusLabel ()

   /**
    * Sets the root flag for this frame.
    *
    * <p>If this frame is the root frame, then when it closes, it executes
    * a System.exit(), causing all frames to close and the application to
    * terminate.
    * </p>
    *
    * @param flag                The new value for the root flag.
    */
   public void setRootFlag (
         boolean flag)
   {
      isRoot = flag;
   } // setRootFlag ()

   // ActionListener interface methods ---------------------------------------
   /**
    * ActionListener event handler method.
    *
    * @param event               The event describing what action occured.
    */
   public void actionPerformed (
         ActionEvent event)
   {
      Object object = event.getSource ();
      
      if ((object == attributeButton) ||
               (object == attributePopupMenuItem))
         attributeButton_Action (event);
      else if (object == classDefPopupMenuItem)
         classDefMenuItem_Action (event);
      else if ((object == lookupButton) ||
          (object == lookupPopupMenuItem))
         lookupButton_Action (event);
      else if (object == renamePopupMenuItem)
         renameMenuItem_Action (event);
      else if (object == schemaPopupMenuItem)
         schemaMenuItem_Action (event);
      else if (object == exitMenuItem)
         dispose ();
      else if (object == lookupMenuItem)
         lookupMenuItem_Action (event);
      else if (object == searchMenuItem)
         searchMenuItem_Action (event);
      else if (object == showClassesMenuItem)
         showClassesMenuItem_Action (event);
      else if (object == refreshMenuItem)
         refreshMenuItem_Action (event);
      else if (object == aboutMenuItem)
         aboutMenuItem_Action (event);
   } // ActionListener.actionPerformed ()

   /**
    * Action handler for the Attribute button and popup menu item.
    *
    * @param event               The event describing what action occured.
    */
   void attributeButton_Action (
         ActionEvent event)
   {
      // Get the selected object
      Object selObj = tree.getLastSelectedPathComponent ();
      
      if ((null == selObj) ||
          (!(selObj instanceof ContextTreeNode)))
      {  // Nothing is selected so we don't need to proceed
         (new MessageBox (
                  "Error",
                  "No item selected.")).show ();
         return;
      }

      try
      {
         ContextTreeNode treeNode = (ContextTreeNode) selObj;
         DirContext dirCtx = (DirContext) treeNode.getThisContext ();
         if (null == dirCtx)
            throw new Exception ("Root node");
         AttributeFrame frame;
         frame = new AttributeFrame (treeNode);
         frame.setTitle ("Attributes");
         // Move the new frame relative to this one
         Point p = getLocation ();
         p.translate (buttonHeight, buttonHeight);
         frame.setLocation (p);
         frame.show ();
      }
      catch (Exception e)
      {
         (new MessageBox (
                  "Error",
                  "Unable to get attributes for object:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
      
   } // attributeButton_Action ()

   /**
    * Action handler for the ClassDef popup menu item.
    *
    * @param event               The event describing what action occured.
    */
   void classDefMenuItem_Action (
         ActionEvent event)
   {
      // Get the selected object
      Object selObj = tree.getLastSelectedPathComponent ();
      
      if ((null == selObj) ||
          (!(selObj instanceof ContextTreeNode)))
      {  // Nothing is selected so we don't need to proceed
         (new MessageBox (
                  "Error",
                  "No item selected.")).show ();
         return;
      }

      try
      {
         ContextTreeNode treeNode = (ContextTreeNode) selObj;
         DirContext dirCtx = (DirContext) treeNode.getThisContext ();
         if (null == dirCtx)
            throw new Exception ("Root node");
         dirCtx = dirCtx.getSchemaClassDefinition ("");
         treeNode = new ContextTreeNode ((ContextFrame) null, dirCtx);
         ContextFrame frame;
         frame = new ContextFrame (treeNode);
         frame.setTitle ("Class Definition");
         // Move the new frame relative to this one
         Point p = getLocation ();
         p.translate (buttonHeight, buttonHeight);
         frame.setLocation (p);
         frame.show ();
      }
      catch (ClassCastException e)
      {
         (new MessageBox (
                  "Error",
                  "Object selected is not a DirContext:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
      catch (Exception e)
      {
         (new MessageBox (
                  "Error",
                  "Unable to get schema for object:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
      
   } // classDefMenuItem_Action()

   /**
    * Action handler for the Lookup button and popup menu item.
    *
    * @param event               The event describing what action occured.
    */
   void lookupButton_Action (
         ActionEvent event)
   {
      // Get the selected object
      Object selObj = tree.getLastSelectedPathComponent ();
      
      if ((null == selObj) ||
          (!(selObj instanceof ContextTreeNode)))
      {  // Nothing is selected so we don't need to proceed
         (new MessageBox (
                  "Error",
                  "No context selected.")).show ();
         return;
      }
      
      Cursor saveCursor = getCursor ();
      setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR));
      
      try
      {
         ContextTreeNode treeNode = (ContextTreeNode) selObj;
         Context ctx = treeNode.getThisContext ();
         if (null == ctx)
            throw new Exception ("Root node");
         NameClassPair ncPair = (NameClassPair) treeNode.getUserObject ();
         ContextTreeNode newRootNode;
         newRootNode = new ContextTreeNode ((ContextFrame) null, ncPair);
         newRootNode.setThisContext (ctx);
         ContextFrame frame = new ContextFrame (newRootNode);
         frame.setStatusLabel ("Context: " + ncPair.getName ());
         Point p = getLocation ();
         p.translate (buttonHeight, buttonHeight);
         frame.setLocation (p);
         frame.show ();
      }
      catch (Exception e)
      {
         (new MessageBox (
                  "Error",
                  "Unable to lookup item:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
      setCursor (saveCursor);
   } // lookupButton_Action ()

   /**
    * Action handler for the Rebind popup menu item.
    *
    * @param event               The event describing what action occured.
    */
/*   void rebindMenuItem_Action (
         ActionEvent event)
   {
      // Get the selected object
      Object selObj = tree.getLastSelectedPathComponent ();
      
      if ((null == selObj) ||
          (!(selObj instanceof ContextTreeNode)))
      {  // Nothing is selected so we don't need to proceed
         (new MessageBox (
                  "Error",
                  "No context selected.")).show ();
         return;
      }
      
      try
      {
         ContextTreeNode treeNode = (ContextTreeNode) selObj;
         Context parentCtx = treeNode.getParentContext ();
         if (null == parentCtx)
            throw new Exception ("No parent context");
            
         NameClassPair ncPair = (NameClassPair) treeNode.getUserObject ();
         RebindFrame frame = new RebindFrame (this, ncPair.getName ());
         Point p = getLocation ();
         p.translate (buttonHeight, buttonHeight);
         frame.setLocation (p);
         frame.show ();
         String newNameStr = frame.getText ();
         frame.dispose ();
         if (null != newNameStr)
         {
            parentCtx.rebind (ncPair.getName (), newNameStr);
            refreshMenuItem_Action ((ActionEvent) null);
         }
      }
      catch (Exception e)
      {
         (new MessageBox (
                  "Error",
                  "Unable to rebind object:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
   } // rebindMenuItem_Action()
*/

   /**
    * Action handler for the Rename popup menu item.
    *
    * @param event               The event describing what action occured.
    */
   void renameMenuItem_Action (
         ActionEvent event)
   {
      // Get the selected object
      Object selObj = tree.getLastSelectedPathComponent ();
      
      if ((null == selObj) ||
          (!(selObj instanceof ContextTreeNode)))
      {  // Nothing is selected so we don't need to proceed
         (new MessageBox (
                  "Error",
                  "No context selected.")).show ();
         return;
      }
      
      try
      {
         ContextTreeNode treeNode = (ContextTreeNode) selObj;
         Context parentCtx = treeNode.getParentContext ();
         if (null == parentCtx)
            throw new Exception ("No parent context");
            
         NameClassPair ncPair = (NameClassPair) treeNode.getUserObject ();
         RenameFrame frame = new RenameFrame (this, ncPair.getName ());
         Point p = getLocation ();
         p.translate (buttonHeight, buttonHeight);
         frame.setLocation (p);
         frame.show ();
         String newNameStr = frame.getText ();
         frame.dispose ();
         if (null != newNameStr)
         {
            parentCtx.rename (ncPair.getName (), newNameStr);
            refreshMenuItem_Action ((ActionEvent) null);
         }
      }
      catch (Exception e)
      {
         (new MessageBox (
                  "Error",
                  "Unable to rename object:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
   } // renameMenuItem_Action()

   /**
    * Action handler for the Schema button.
    *
    * @param event               The event describing what action occured.
    */
   void schemaMenuItem_Action (
         ActionEvent event)
   {
      // Get the selected object
      Object selObj = tree.getLastSelectedPathComponent ();
      
      if ((null == selObj) ||
          (!(selObj instanceof ContextTreeNode)))
      {  // Nothing is selected so we don't need to proceed
         (new MessageBox (
                  "Error",
                  "No item selected.")).show ();
         return;
      }

      try
      {
         ContextTreeNode treeNode = (ContextTreeNode) selObj;
         DirContext dirCtx = (DirContext) treeNode.getThisContext ();
         if (null == dirCtx)
            throw new Exception ("Root node");
         dirCtx = dirCtx.getSchema ("");
         treeNode = new ContextTreeNode ((ContextFrame) null, dirCtx);
         ContextFrame frame;
         frame = new ContextFrame (treeNode);
         frame.setTitle ("Schema");
         // Move the new frame relative to this one
         Point p = getLocation ();
         p.translate (buttonHeight, buttonHeight);
         frame.setLocation (p);
         frame.show ();
      }
      catch (ClassCastException e)
      {
         (new MessageBox (
                  "Error",
                  "Object selected is not a DirContext:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
      catch (Exception e)
      {
         (new MessageBox (
                  "Error",
                  "Unable to get schema for object:\n" +
                  Util.getExceptionTrace (e))).show ();
      }
      
   } // schemaMenuItem_Action()

   /**
    * Action handler for the lookup menu item.
    *
    * @param event               The event describing what action occured.
    */
   public void lookupMenuItem_Action (
         ActionEvent event)
   {
      LookupFrame lookupFrame = new LookupFrame (this);
      lookupFrame.show ();
      String lookupStr = lookupFrame.getText ();
      lookupFrame.dispose ();
      if (null != lookupStr)
      {
         try
         {
            Context ctx = (Context) rootNode.getThisContext ();
            Object obj = ctx.lookup (lookupStr);
            if (obj instanceof Context)
            {
               ContextTreeNode newRootNode;
               newRootNode = new ContextTreeNode ((ContextFrame) null, (Context) obj);
               ContextFrame frame = new ContextFrame (newRootNode);
               frame.setStatusLabel ("Context: " + lookupStr);
               Point p = getLocation ();
               p.translate (buttonHeight, buttonHeight);
               frame.setLocation (p);
               frame.show ();
            }
            else
            {
               (new MessageBox (
                        "Lookup",
                        "Found non-context object:\n" + obj.toString ())).show ();
            }
         }
         catch (NamingException e)
         {
            (new MessageBox (
                     "Error",
                     "Unable to lookup object:\n" +
                     Util.getExceptionTrace (e))).show ();
         }
      }
   } // lookupMenuItem_Action ()

   /**
    * Action handler for the search menu item.
    *
    * @param event               The event describing what action occured.
    */
   public void searchMenuItem_Action (
         ActionEvent event)
   {
      SearchFrame searchFrame = new SearchFrame (this);
      searchFrame.show ();
      String searchStr = searchFrame.getText ();
      searchFrame.dispose ();
      if (null != searchStr)
      {
         try
         {
            DirContext dirCtx = (DirContext) rootNode.getThisContext ();
            SearchControls controls = new SearchControls (
                           SearchControls.ONELEVEL_SCOPE,
                           0,                               // countlim
                           0,                               // timelim
                           new String[0],                   // attrs
                           false,                           // retobj
                           false);                          // deref
                           
            NamingEnumeration enum = dirCtx.search ("", searchStr, controls);
            SearchResult result;
            SearchTreeNode newRootNode;

            newRootNode = new SearchTreeNode (
                                 (ContextFrame) null,
                                 dirCtx,
                                 enum);
            ContextFrame frame = new ContextFrame (newRootNode);
            frame.setStatusLabel ("Search: " + searchStr);
            frame.setTitle ("Search Result");
            Point p = getLocation ();
            p.translate (buttonHeight, buttonHeight);
            frame.setLocation (p);
            frame.show ();
         }
         catch (ClassCastException e)
         {
            (new MessageBox (
                     "Error",
                     "Root object is not a DirContext:\n" +
                     Util.getExceptionTrace (e))).show ();
         }
         catch (NamingException e)
         {
            (new MessageBox (
                     "Error",
                     "Unable to perform search:\n" +
                     Util.getExceptionTrace (e))).show ();
         }
      }
   } // searchMenuItem_Action ()

   /**
    * Action handler for the show classes menu item.
    *
    * @param event               The event describing what action occured.
    */
   public void showClassesMenuItem_Action (
         ActionEvent event)
   {
      ContextTreeNode.showClasses = !ContextTreeNode.showClasses;
      refreshMenuItem_Action (event);
   } // showClassesMenuItem_Action()

   /**
    * Action handler for the refresh menu item.
    *
    * @param event               The event describing what action occured.
    */
   public void refreshMenuItem_Action (
         ActionEvent event)
   {
      rootNode.removeAllChildren ();
      treeModel.reload ();
   } // refreshMenuItem_Action()

   /**
    * Action handler for the about menu item.
    *
    * @param event               The event describing what action occured.
    */
   public void aboutMenuItem_Action (
         ActionEvent event)
   {
      Frame frame = AboutFrame.getAboutFrame ((URL) null);
      Point p = getLocation ();
      p.translate (buttonHeight, buttonHeight);
      frame.setLocation (p);
      frame.show ();
   } // aboutMenuItem_Action()

   /**
    * Overrides the dispose method to exit the app if the root browser is
    * closed.
    */
   public void dispose ()
   {
      try
      {  // Apparently this doesn't work from an archive
         super.dispose ();
         if (isRoot)
            System.exit (0);
      }
      catch (Throwable e)
      {
      }
   } // dispose ()

   // MouseListener interface methods =======================================
   /**
    * Invoked when the mouse button has been clicked on a component.
    */
   public void mouseClicked (
         MouseEvent e)
   {
   } // mouseClicked ()

   /**
    * Invoked when the mouse enters a component.
    */
   public void mouseEntered (
         MouseEvent e)
   {
   } // mouseEntered ()
         
   /**
    * Invoked when the mouse exits a component.
    */
   public void mouseExited (
         MouseEvent e)
   {
   } // mouseExited ()
         
   /**
    * Invoked when the mouse button has been pressed on a component.
    */
   public void mousePressed (
         MouseEvent e)
   {
      maybeShowPopup (e);
   } // mousePressed ()

   /**
    * Invoked when the mouse button has been released on a component.
    */
   public void mouseReleased (
         MouseEvent e)
   {
      maybeShowPopup (e);
   } // mouseReleased ()

   /**
    * Common method to handle mouse popup event.
    */
   private void maybeShowPopup (
         MouseEvent e)
   {
      if (e.isPopupTrigger ())
      {
         Object component = e.getComponent ();
         Object selObj;
         if (component == tree)
         {
            selObj = tree.getLastSelectedPathComponent ();
            if (null == selObj)     // Nothing is selected so get out
               return;
            
            popup = new JPopupMenu ();
            popup.add (attributePopupMenuItem);
            popup.add (classDefPopupMenuItem);
            popup.add (lookupPopupMenuItem);
            popup.add (renamePopupMenuItem);
            popup.add (schemaPopupMenuItem);
            if (null != NJCLApplet.popupHandlers)
            {
               popup.addSeparator ();
               for (int i = 0; i < NJCLApplet.popupHandlers.length; i++)
                  NJCLApplet.popupHandlers[i].popupRequested (this, popup, selObj);
            }
            popup.show (e.getComponent (), e.getX (), e.getY ());
         }
      }
   } // maybeShowPopup ()

} // class ContextFrame

