Package net.sourceforge.squirrel_sql.client.session.mainpanel.objecttree

Source Code of net.sourceforge.squirrel_sql.client.session.mainpanel.objecttree.ObjectTree$TreeLoader

package net.sourceforge.squirrel_sql.client.session.mainpanel.objecttree;
/*
* Copyright (C) 2002-2004 Colin Bell
* colbell@users.sourceforge.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write toS the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

import net.sourceforge.squirrel_sql.client.action.ActionCollection;
import net.sourceforge.squirrel_sql.client.gui.db.SQLAliasColorProperties;
import net.sourceforge.squirrel_sql.client.session.ISession;
import net.sourceforge.squirrel_sql.client.session.action.CopyQualifiedObjectNameAction;
import net.sourceforge.squirrel_sql.client.session.action.CopySimpleObjectNameAction;
import net.sourceforge.squirrel_sql.client.session.action.DeleteSelectedTablesAction;
import net.sourceforge.squirrel_sql.client.session.action.EditWhereColsAction;
import net.sourceforge.squirrel_sql.client.session.action.FilterObjectsAction;
import net.sourceforge.squirrel_sql.client.session.action.RefreshObjectTreeItemAction;
import net.sourceforge.squirrel_sql.client.session.action.RefreshSchemaInfoAction;
import net.sourceforge.squirrel_sql.client.session.action.SQLFilterAction;
import net.sourceforge.squirrel_sql.client.session.action.SetDefaultCatalogAction;
import net.sourceforge.squirrel_sql.fw.gui.CursorChanger;
import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
import net.sourceforge.squirrel_sql.fw.id.IIdentifier;
import net.sourceforge.squirrel_sql.fw.sql.DatabaseObjectInfo;
import net.sourceforge.squirrel_sql.fw.sql.DatabaseObjectType;
import net.sourceforge.squirrel_sql.fw.sql.IDatabaseObjectInfo;
import net.sourceforge.squirrel_sql.fw.sql.ITableInfo;
import net.sourceforge.squirrel_sql.fw.util.EnumerationIterator;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
/**
* This is the tree showing the structure of objects in the database.
*
* @author <A HREF="mailto:colbell@users.sourceforge.net">Colin Bell</A>
*/
class ObjectTree extends JTree
{
    private static final long serialVersionUID = 1L;

    /** Logger for this class. */
  private static final ILogger s_log =
    LoggerController.createLogger(ObjectTree.class);

  /** Model for this tree. */
  private final ObjectTreeModel _model;

  /** Current session. */
  transient private final ISession _session;

  /**
   * Collection of popup menus (<TT>JPopupMenu</TT> instances) for the
   * object tree. Keyed by node type.
   */
  private final Map<IIdentifier, JPopupMenu> _popups =
        new HashMap<IIdentifier, JPopupMenu>();

  /**
   * Global popup menu. This contains items that are to be displayed
   * in the popup menu no matter what items are selected in the tree.
   */
  private final JPopupMenu _globalPopup = new JPopupMenu();

  private final List<Action> _globalActions = new ArrayList<Action>();

  /**
   * Object to synchronize on so that only one node can be expanded at any
   * one time.
   */
  private Object _syncObject = new Object();

  /**
   * String representation of the <TT>TreePath</TT> objects that have been
   * expanded. The key is <TT>Treepath.toString()</TT> and the value
   * is <TT>null</TT>.
   */
  private Map<String, Object> _expandedPathNames = new HashMap<String, Object>();

  /**
   * Collection of listeners to this object tree.
   */
  private EventListenerList _listenerList = new EventListenerList();

   private boolean _startExpandInThread = true;

   /**
    * ctor specifying session.
    *
    * @param  session  Current session.
    *
    * @throws  IllegalArgumentException
    *       Thrown if <TT>null</TT> <TT>ISession</TT> passed.
    */
   ObjectTree(ISession session)
   {
      super(new ObjectTreeModel(session));
      if (session == null)
      {
         throw new IllegalArgumentException("ISession == null");
      }
      setRowHeight(getFontMetrics(getFont()).getHeight());
      _session = session;
      _model = (ObjectTreeModel)getModel();
      setModel(_model);

      addTreeExpansionListener(new NodeExpansionListener());

      addTreeSelectionListener(new TreeSelectionListener()
      {
         public void valueChanged(TreeSelectionEvent e)
         {
            if(null != e.getNewLeadSelectionPath())
            {
               scrollPathToVisible(e.getNewLeadSelectionPath());
            }
         }
      });

      setShowsRootHandles(true);

      // Add actions to the popup menu.
      final ActionCollection actions = session.getApplication().getActionCollection();

      // Options for global popup menu.
      addToPopup(actions.get(RefreshSchemaInfoAction.class));
      addToPopup(actions.get(RefreshObjectTreeItemAction.class));

      addToPopup(DatabaseObjectType.TABLE, actions.get(EditWhereColsAction.class));

      addToPopup(DatabaseObjectType.TABLE, actions.get(SQLFilterAction.class));
      addToPopup(DatabaseObjectType.VIEW, actions.get(SQLFilterAction.class));

      addToPopup(DatabaseObjectType.TABLE, actions.get(DeleteSelectedTablesAction.class));

      addToPopup(DatabaseObjectType.SESSION, actions.get(FilterObjectsAction.class));


      session.getApplication().getThreadPool().addTask(new Runnable() {
          public void run() {
            try
            {
                // Option to select default catalog only applies to sessions
                // that support catalogs.
                if (_session.getSQLConnection().getSQLMetaData().supportsCatalogs())
                {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            addToPopup(DatabaseObjectType.CATALOG,
                                       actions.get(SetDefaultCatalogAction.class));
                        }

                    });
                }
            }
            catch (Throwable th)
            {
                // Assume DBMS doesn't support catalogs.
                s_log.debug(th);
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    addToPopup(actions.get(CopySimpleObjectNameAction.class));
                    addToPopup(actions.get(CopyQualifiedObjectNameAction.class));


                  addMouseListener(new ObjectTreeMouseListener());
                  setCellRenderer(new ObjectTreeCellRenderer(_model, _session));
                  ObjectTree.this.refresh(false);
                  ObjectTree.this.setSelectionPath(ObjectTree.this.getPathForRow(0));
                }
            });
          }
      });

      SQLAliasColorProperties colorProps = session.getAlias().getColorProperties();
      if (colorProps.isOverrideObjectTreeBackgroundColor()) {
        int rgbValue = colorProps.getObjectTreeBackgroundColorRgbValue();
        setBackground(new Color(rgbValue));
      }
   }

   // Mouse listener used to display popup menu.
   private class ObjectTreeMouseListener extends MouseAdapter {
      public void mousePressed(MouseEvent evt)
      {       
        
          if (evt.isPopupTrigger())
          {
          // If the user wants to select for Right mouse clicks then change the selection before popup
           // appears
            if (_session.getApplication().getSquirrelPreferences().getSelectOnRightMouseClick()) {           
              TreePath path = ObjectTree.this.getPathForLocation(evt.getX(), evt.getY());
              boolean alreadySelected = false;
              TreePath[] selectedPaths = ObjectTree.this.getSelectionPaths();
              if (selectedPaths != null) {             
                for (TreePath selectedPath : selectedPaths) {
                  if (path != null && path.equals(selectedPath)) {
                    alreadySelected = true;
                    break;
                  }
                }
              }
              if (!alreadySelected) {
                ObjectTree.this.setSelectionPath(path);
              }
            }
             showPopup(evt.getX(), evt.getY());
          }
      }
      public void mouseReleased(MouseEvent evt)
      {
          if (evt.isPopupTrigger())
          {
              showPopup(evt.getX(), evt.getY());
          }
      }    
   }
  
  /**
   * Component has been added to its parent.
   */
  public void addNotify()
  {
    super.addNotify();
    // Register so that we can display different tooltips depending
    // which entry in list mouse is over.
    ToolTipManager.sharedInstance().registerComponent(this);
  }

  /**
   * Component has been removed from its parent.
   */
  public void removeNotify()
  {
    super.removeNotify();

    // Don't need tooltips any more.
    ToolTipManager.sharedInstance().unregisterComponent(this);
  }

  /**
   * Return the name of the object that the mouse is currently
   * over as the tooltip text.
   *
   * @param  event  Used to determine the current mouse position.
   */
  public String getToolTipText(MouseEvent evt)
  {
    String tip = null;
    final TreePath path = getPathForLocation(evt.getX(), evt.getY());
    if (path != null)
    {
      tip = path.getLastPathComponent().toString();
    }
    else
    {
      tip = getToolTipText();
    }
    return tip;
  }

  /**
   * Return the typed data model for this tree.
   *
   * @return  The typed data model for this tree.
   */
  public ObjectTreeModel getTypedModel()
  {
    return _model;
  }

  /**
   * Refresh tree.
    * @param reloadSchemaInfo
    */
  public void refresh(final boolean reloadSchemaInfo)
  {
      Runnable task = new Runnable()
      {
         public void run()
         {
            if (reloadSchemaInfo)
            {
               _session.getSchemaInfo().reloadAll();
            }


            GUIUtils.processOnSwingEventThread(new Runnable()
            {
               public void run()
               {
                  refreshTree();
               }
            });
         }
      };

      if(reloadSchemaInfo)
      {
         _session.getApplication().getThreadPool().addTask(task);
      }
      else
      {
         // No need to this in background when SchemaInfo  is not reloaded.
         task.run();
      }
   }

   private void refreshTree()
   {
      final TreePath[] selectedPaths = getSelectionPaths();
      final Map<String, Object> selectedPathNames =
          new HashMap<String, Object>();
      if (selectedPaths != null)
      {
         for (int i = 0; i < selectedPaths.length; ++i)
         {
            selectedPathNames.put(selectedPaths[i].toString(), null);
         }
      }
      ObjectTreeNode root = _model.getRootObjectTreeNode();
      root.removeAllChildren();
      fireObjectTreeCleared();
      startExpandingTree(root, false, selectedPathNames, false);
      fireObjectTreeRefreshed();
   }

   /**
    * Refresh the nodes currently selected in the object tree.
    */
   public void refreshSelectedNodes()
   {

      final TreePath[] selectedPaths = getSelectionPaths();
      ObjectTreeNode[] nodes = getSelectedNodes();
      final Map<String, Object> selectedPathNames =
          new HashMap<String, Object>();
      if (selectedPaths != null)
      {
         for (int i = 0; i < selectedPaths.length; ++i)
         {
            selectedPathNames.put(selectedPaths[i].toString(), null);
         }
      }
      clearSelection();


      DefaultMutableTreeNode parent = (DefaultMutableTreeNode) nodes[0].getParent();

      if (parent != null)
      {
         parent.removeAllChildren();
         startExpandingTree((ObjectTreeNode) parent, false, selectedPathNames, true);
      }
      else
      {
         nodes[0].removeAllChildren();
         startExpandingTree(nodes[0], false, selectedPathNames, true);
      }
   }

   /**
   * Adds a listener for changes in this cache entry.
   *
   * @param  lis  a IObjectCacheChangeListener that will be notified when
   *        objects are added and removed from this cache entry.
   */
  public void addObjectTreeListener(IObjectTreeListener lis)
  {
    _listenerList.add(IObjectTreeListener.class, lis);
  }

  /**
   * Removes a listener for changes in this cache entry.
   *
   * @param  lis a IObjectCacheChangeListener that will be notified when
   *      objects are added and removed from this cache entry.
   */
  void removeObjectTreeListener(IObjectTreeListener lis)
  {
    _listenerList.remove(IObjectTreeListener.class, lis);
  }

  /**
   * Restore the expansion state of the tree starting at the passed node.
   * The passed node is always expanded.
   *
   * @param  node  Node to restore expansion state from.
   *
   * @throws  IllegalArgumentException
   *       Thrown if null ObjectTreeNode passed.
   */
  private void restoreExpansionState(ObjectTreeNode node,
                                     Map<String, Object> previouslySelectedTreePathNames,
                                       List<TreePath> selectedTreePaths)
  {
    if (node == null)
    {
      throw new IllegalArgumentException("ObjectTreeNode == null");
    }

    final TreePath nodePath = new TreePath(node.getPath());
        if (matchKeyPrefix(previouslySelectedTreePathNames, node, nodePath.toString()))
    {
      selectedTreePaths.add(nodePath);
    }


      try
      {
         _startExpandInThread = false;
         expandPath(nodePath);
      }
      finally
      {
         _startExpandInThread = true;
      }



      // Go through each child of the parent and see if it was previously
    // expanded. If it was recursively call this method in order to expand
    // the child.
      @SuppressWarnings("unchecked")
      Enumeration<ObjectTreeNode> childEnumeration =
          (Enumeration<ObjectTreeNode>) node.children();
    Iterator<ObjectTreeNode> it =
            new EnumerationIterator<ObjectTreeNode>(childEnumeration);
    while (it.hasNext())
    {
      final ObjectTreeNode child = it.next();
      final TreePath childPath = new TreePath(child.getPath());
      final String childPathName = childPath.toString();

         if (matchKeyPrefix(previouslySelectedTreePathNames, child, childPathName))
      {
        selectedTreePaths.add(childPath);
      }

      if (_expandedPathNames.containsKey(childPathName))
      {
        restoreExpansionState(child, previouslySelectedTreePathNames, selectedTreePaths);
         }
    }
  }

    /**
     * This is to handle the case where the user has enabled showRowCounts and
     * the table/view name as it appeared before is different only because the
     * number of rows has changed. For example, suppose a user deletes records
     * in a table "foo" with 100 rows then refreshes the tree.  The tree node
     * before the delete looks like foo(100) and after looks like foo(0).  We
     * want to strip off the (...) and test to see if the selected path "foo"
     * is the same before the delete as after.  This way, when the user refreshes
     * "foo(...)", then it is still selected after the refresh.
     *
     * @param map
     * @param pattern
     * @return
     */
    protected boolean matchKeyPrefix(Map<String, Object> map, ObjectTreeNode node, String path) {
        // We only show row counts for tables and views.  Other objects won't
        // be affected by changing row counts.
        if (node.getDatabaseObjectType() != DatabaseObjectType.TABLE
                && node.getDatabaseObjectType() != DatabaseObjectType.VIEW)
        {
            return map.containsKey(path);
        }
        Set<String> s = map.keySet();
        Iterator<String> i = s.iterator();
        String pathPrefix = path;
        if (path.indexOf("(") != -1) {
            pathPrefix = path.substring(0, path.lastIndexOf("("));
        }
        boolean result = false;
        while (i.hasNext()) {
            String key = i.next();
            String keyPrefix = key;
            if (key.indexOf("(") != -1) {
                keyPrefix = key.substring(0, key.lastIndexOf("("));
            }
            if (keyPrefix.equals(pathPrefix)) {
                result = true;
                break;
            }
        }
        return result;
    }
       
  private void startExpandingTree(ObjectTreeNode node,
                                   boolean selectNode,
                                   Map<String, Object> selectedPathNames,
                                   boolean refreshSchemaInfo
   )
  {
    ExpansionController exp = new ExpansionController(node, selectNode, selectedPathNames, refreshSchemaInfo);
      exp.run();
  }

  private void expandNode(ObjectTreeNode node, boolean selectNode)
  {
    if (node == null)
    {
      throw new IllegalArgumentException("ObjectTreeNode == null");
    }
    // If node hasn't already been expanded.
    if (node.getChildCount() == 0)
    {
      // Add together the standard expanders for this node type and any
      // individual expanders that there are for the node and process them.
      final DatabaseObjectType dboType = node.getDatabaseObjectType();
      INodeExpander[] stdExpanders = _model.getExpanders(dboType);
      INodeExpander[] extraExpanders = node.getExpanders();
      if (stdExpanders.length > 0 || extraExpanders.length > 0)
      {
        INodeExpander[] expanders = null;
        if (stdExpanders.length > 0 && extraExpanders.length == 0)
        {
          expanders = stdExpanders;
        }
        else if (stdExpanders.length == 0 && extraExpanders.length > 0)
        {
          expanders = extraExpanders;
        }
        else
        {
          expanders = new INodeExpander[stdExpanders.length + extraExpanders.length];
          System.arraycopy(stdExpanders, 0, expanders, 0, stdExpanders.length);
          System.arraycopy(extraExpanders, 0, expanders, stdExpanders.length,
                    extraExpanders.length);
        }
        new TreeLoader(node, expanders, selectNode).execute();
      }
    }
  }

  /**
   * Add an item to the popup menu for the specified node type in the object
   * tree.
   *
   * @param  dboType    Database Object Type.
   * @param  action    Action to add to menu.
   *
   * @throws  IllegalArgumentException
   *       Thrown if a <TT>null</TT> <TT>Action</TT> or
   *      <TT>DatabaseObjectType</TT>thrown.
   */
  void addToPopup(DatabaseObjectType dboType, Action action)
  {
    if (dboType == null)
    {
      throw new IllegalArgumentException("Null DatabaseObjectType passed");
    }
    if (action == null)
    {
      throw new IllegalArgumentException("Null Action passed");
    }

    final JPopupMenu pop = getPopup(dboType, true);
    pop.add(action);
  }

  /**
   * Add an item to the popup menu for the all nodes.
   *
   * @param  action    Action to add to menu.
   *
   * @throws  IllegalArgumentException
   *       Thrown if a <TT>null</TT> <TT>Action</TT> thrown.
   */
  void addToPopup(Action action)
  {
    if (action == null)
    {
      throw new IllegalArgumentException("Null Action passed");
    }
    _globalPopup.add(action);
    _globalActions.add(action);

    for (Iterator<JPopupMenu> it = _popups.values().iterator(); it.hasNext();)
    {
      JPopupMenu pop = it.next();
      pop.add(action);
    }
  }

  /**
   * Add an hierarchical menu to the popup menu for the specified database
   * object type.
   *
   * @param  dboType    Database object type.
   * @param  menu    <TT>JMenu</TT> to add to menu.
   *
   * @throws  IllegalArgumentException
   *       Thrown if a <TT>null</TT> <TT>DatabaseObjectType</TT> or
   *       <TT>JMenu</TT> thrown.
   */
  public void addToPopup(DatabaseObjectType dboType, JMenu menu)
  {
    if (dboType == null)
    {
      throw new IllegalArgumentException("DatabaseObjectType == null");
    }
    if (menu == null)
    {
      throw new IllegalArgumentException("JMenu == null");
    }

    final JPopupMenu pop = getPopup(dboType, true);
    pop.add(menu);
  }

  /**
   * Add an hierarchical menu to the popup menu for all node types.
   *
   * @param  menu  <TT>JMenu</TT> to add to menu.
   *
   * @throws  IllegalArgumentException
   *       Thrown if a <TT>null</TT> <TT>JMenu</TT> thrown.
   */
  public void addToPopup(JMenu menu)
  {
    if (menu == null)
    {
      throw new IllegalArgumentException("JMenu == null");
    }
    _globalPopup.add(menu);
    _globalActions.add(menu.getAction());

    for (Iterator<JPopupMenu> it = _popups.values().iterator(); it.hasNext();)
    {
      JPopupMenu pop = it.next();
      pop.add(menu);
    }
  }

  /**
   * Get the popup menu for the passed database object type. If one
   * doesn't exist then create one if requested to do so.

   * @param  dboType    Database Object Type.
   * @param  create    If <TT>true</TT> popup will eb created if it
   *            doesn't exist.
   *
   * @throws  IllegalArgumentException
   *       Thrown if a <TT>null</TT> <TT>Action</TT> or
   *      <TT>DatabaseObjectType</TT>thrown.
   */
  private JPopupMenu getPopup(DatabaseObjectType dboType, boolean create)
  {
    if (dboType == null)
    {
      throw new IllegalArgumentException("Null DatabaseObjectType passed");
    }
    IIdentifier key = dboType.getIdentifier();
    JPopupMenu pop = _popups.get(key);
    if (pop == null && create)
    {
      pop = new JPopupMenu();
      _popups.put(key, pop);
      for (Iterator<Action> it = _globalActions.iterator(); it.hasNext();)
      {
        pop.add(it.next());
      }
    }
    return pop;
  }

  /**
   * Return an array of the currently selected nodes. This array is sorted
   * by the simple name of the database object.
   *
   * @return  array of <TT>ObjectTreeNode</TT> objects.
   */
  ObjectTreeNode[] getSelectedNodes()
  {
    TreePath[] paths = getSelectionPaths();
    List<ObjectTreeNode> list = new ArrayList<ObjectTreeNode>();
    if (paths != null)
    {
      for (int i = 0; i < paths.length; ++i)
      {
        Object obj = paths[i].getLastPathComponent();
        if (obj instanceof ObjectTreeNode)
        {
          list.add((ObjectTreeNode)obj);
        }
      }
    }
    ObjectTreeNode[] ar = list.toArray(new ObjectTreeNode[list.size()]);
    Arrays.sort(ar, new NodeComparator());
    return ar;
  }
   
  /**
   * Return an array of the currently selected database
   * objects.
   *
   * @return  array of <TT>ObjectTreeNode</TT> objects.
   */
  IDatabaseObjectInfo[] getSelectedDatabaseObjects()
  {
    ObjectTreeNode[] nodes = getSelectedNodes();
    IDatabaseObjectInfo[] dbObjects = new IDatabaseObjectInfo[nodes.length];
    for (int i = 0; i < nodes.length; ++i)
    {
      dbObjects[i] = nodes[i].getDatabaseObjectInfo();
    }
    return dbObjects;
  }

    /**
     * Return a type-safe list of the currently selected database tables
     *
     * @return  list of <TT>ITableInfo</TT> objects.
     */
    List<ITableInfo> getSelectedTables()
    {
        ObjectTreeNode[] nodes = getSelectedNodes();
        ArrayList<ITableInfo> result = new ArrayList<ITableInfo>();
        for (int i = 0; i < nodes.length; ++i)
        {
            if (nodes[i].getDatabaseObjectType() == DatabaseObjectType.TABLE) {
                result.add((ITableInfo)nodes[i].getDatabaseObjectInfo());
            }
        }
        return result;
    }
   
   
  /**
   * Get the appropriate popup menu for the currently selected nodes
   * in the object tree and display it.
   *
   * @param  x  X pos to display popup at.
   * @param  y  Y pos to display popup at.
   */
  private void showPopup(int x, int y)
  {
    ObjectTreeNode[] selObj = getSelectedNodes();
    if (selObj.length > 0)
    {
      // See if all selected nodes are of the same type.
      boolean sameType = true;
      final DatabaseObjectType dboType = selObj[0].getDatabaseObjectType();
      for (int i = 1; i < selObj.length; ++i)
      {
        if (selObj[i].getDatabaseObjectType() != dboType)
        {
          sameType = false;
          break;
        }
      }

      JPopupMenu pop = null;
      if (sameType)
      {
        pop = getPopup(dboType, false);
      }
      if (pop == null)
      {
        pop = _globalPopup;
      }
      pop.show(this, x, y);
    }
  }

  /**
   * Fire a "tree cleared" event to all listeners.
   */
  private void fireObjectTreeCleared()
  {
    // Guaranteed to be non-null.
    Object[] listeners = _listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event.
    ObjectTreeListenerEvent evt = null;
    for (int i = listeners.length - 2; i >= 0; i-=2 )
    {
      if (listeners[i] == IObjectTreeListener.class)
      {
        // Lazily create the event.
        if (evt == null)
        {
          evt = new ObjectTreeListenerEvent(ObjectTree.this);
        }
        ((IObjectTreeListener)listeners[i + 1]).objectTreeCleared(evt);
      }
    }
  }

  /**
   * Fire a "tree refreshed" event to all listeners.
   */
  private void fireObjectTreeRefreshed()
  {
    // Guaranteed to be non-null.
    Object[] listeners = _listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event.
    ObjectTreeListenerEvent evt = null;
    for (int i = listeners.length - 2; i >= 0; i-=2 )
    {
      if (listeners[i] == IObjectTreeListener.class)
      {
        // Lazily create the event.
        if (evt == null)
        {
          evt = new ObjectTreeListenerEvent(ObjectTree.this);
        }
        ((IObjectTreeListener)listeners[i + 1]).objectTreeRefreshed(evt);
      }
    }
  }

  public void dispose()
  {
    // Menues that are also shown in the main window Session menu might
    // be in this popup. If we don't remove them, the Session won't be Garbage Collected.
    _globalPopup.removeAll();
    _globalPopup.setInvoker(null);
    _globalActions.clear();
    for(Iterator<JPopupMenu> i=_popups.values().iterator(); i.hasNext();)
    {
      JPopupMenu popup = i.next();
      popup.removeAll();
      popup.setInvoker(null);
    }
    _popups.clear();
  }

  private final class NodeExpansionListener implements TreeExpansionListener
  {
    public void treeExpanded(TreeExpansionEvent evt)
    {
      // Get the node to be expanded.
      final TreePath path = evt.getPath();
      final Object parentObj = path.getLastPathComponent();
      if (parentObj instanceof ObjectTreeNode)
      {
        startExpandingTree((ObjectTreeNode)parentObj, false, null, false);
        _expandedPathNames.put(path.toString(), null);
      }
    }

    public void treeCollapsed(TreeExpansionEvent evt)
    {
      _expandedPathNames.remove(evt.getPath().toString());
    }
  }

  /**
   * This class is used to sort the nodes by their title.
   */
  private static class NodeComparator implements Comparator<ObjectTreeNode>,
                                                   Serializable
  {
        private static final long serialVersionUID = 1L;

        public int compare(ObjectTreeNode obj1, ObjectTreeNode obj2)
    {
      return obj1.toString().compareToIgnoreCase(obj2.toString());
    }
  }

  private class ExpansionController implements Runnable
  {
    private final ObjectTreeNode _node;
    private final boolean _selectNode;
    private final Map<String, Object> _selectedPathNames;
      private boolean _refreshSchemaInfo;

      ExpansionController(ObjectTreeNode node,
                          boolean selectNode,
                          Map<String, Object> selectedPathNames,
                          boolean refreshSchemaInfo)
      {
         super();
         _node = node;
         _selectNode = selectNode;
         _selectedPathNames = selectedPathNames;
         _refreshSchemaInfo = refreshSchemaInfo;
      }

    public void run()
    {
      synchronized (ObjectTree.this._syncObject)
      {
        CursorChanger cursorChg = new CursorChanger(ObjectTree.this);
        cursorChg.show();
        try
        {
               if(_refreshSchemaInfo)
               {
                  _session.getSchemaInfo().reload(_node.getDatabaseObjectInfo());
               }

               expandNode(_node, _selectNode);
          if (_selectedPathNames != null)
          {
            final List<TreePath> newlySelectedTreepaths = new ArrayList<TreePath>();
           
            GUIUtils.processOnSwingEventThread(new Runnable()
            {
              public void run()
              {
                        restoreExpansionState(_node, _selectedPathNames, newlySelectedTreepaths);
                        setSelectionPaths(newlySelectedTreepaths.toArray(new TreePath[newlySelectedTreepaths.size()]));
                     }
            });
          }
        }
        finally
        {
          cursorChg.restore();
        }
      }
    }
  }

  /**
   * This class actually loads the tree.
   */
  private final class TreeLoader
  {
    private ObjectTreeNode _parentNode;
    private INodeExpander[] _expanders;
    private boolean _selectParentNode;

    TreeLoader(ObjectTreeNode parentNode, INodeExpander[] expanders,
          boolean selectParentNode)
    {
      super();
      _parentNode = parentNode;
      _expanders = expanders;
      _selectParentNode= selectParentNode;
    }

    void execute()
    {
      try
      {
        try
        {
          ObjectTreeNode loadingNode = showLoadingNode();
          try
          {
            loadChildren();
          }
          finally
          {
                        if (_parentNode.isNodeChild(loadingNode)){
                            _parentNode.remove(loadingNode);
                        }
          }
        }
        finally
        {
          fireStructureChanged(_parentNode);
          if (_selectParentNode)
          {
            clearSelection();
            setSelectionPath(new TreePath(_parentNode.getPath()));
          }
        }
      }
      catch (Throwable ex)
      {
        final String msg = "Error: " + _parentNode.toString();
        s_log.error(msg, ex);
        _session.showErrorMessage(msg + ": " + ex.toString());
      }
    }

    /**
     * This adds a node to the tree that says "Loading..." in order to give
     * feedback to the user.
     */
    private ObjectTreeNode showLoadingNode()
    {
      IDatabaseObjectInfo doi = new DatabaseObjectInfo(null, null,
                "Loading...", DatabaseObjectType.OTHER,
                _session.getSQLConnection().getSQLMetaData());
      ObjectTreeNode loadingNode = new ObjectTreeNode(_session, doi);
      _parentNode.add(loadingNode);
      fireStructureChanged(_parentNode);
      return loadingNode;
    }

    /**
     * This expands the parent node and shows all its children.
     */
    private void loadChildren() throws SQLException
    {
      for (int i = 0; i < _expanders.length; ++i)
      {
        boolean nodeTypeAllowsChildren = false;
        DatabaseObjectType lastDboType = null;
        List<ObjectTreeNode> list = _expanders[i].createChildren(_session, _parentNode);
        Iterator<ObjectTreeNode> it = list.iterator();
        while (it.hasNext())
        {
          Object nextObj = it.next();
          if (nextObj instanceof ObjectTreeNode)
          {
            ObjectTreeNode childNode = (ObjectTreeNode)nextObj;
            if (childNode.getExpanders().length >0)
            {
              childNode.setAllowsChildren(true);
            }
            else
            {
              DatabaseObjectType childNodeDboType = childNode.getDatabaseObjectType();
              if (childNodeDboType != lastDboType)
              {
                getTypedModel().addKnownDatabaseObjectType(childNodeDboType);
                lastDboType = childNodeDboType;
                if (_model.getExpanders(childNodeDboType).length > 0)
                {
                  nodeTypeAllowsChildren = true;
                }
                else
                {
                  nodeTypeAllowsChildren = false;
                }
              }
              childNode.setAllowsChildren(nodeTypeAllowsChildren);
            }
            _parentNode.add(childNode);
          }
        }
      }
    }

    /**
     * Let the object tree model know that its structure has changed.
     */
    private void fireStructureChanged(final ObjectTreeNode node)
    {
      GUIUtils.processOnSwingEventThread(new Runnable()
      {
        public void run()
        {
          ObjectTree.this._model.nodeStructureChanged(node);
        }
      });
    }
  }
}
TOP

Related Classes of net.sourceforge.squirrel_sql.client.session.mainpanel.objecttree.ObjectTree$TreeLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.