FilterBuilder.java
   1  // HTMLParser Library $Name: v1_6_20060319 $ - A java-based parser for HTML
   2  // http://sourceforge.org/projects/htmlparser
   3  // Copyright (C) 2005 Derrick Oswald
   4  //
   5  // Revision Control Information
   6  //
   7  // $Source: /cvsroot/htmlparser/htmlparser/src/org/htmlparser/parserapplications/filterbuilder/FilterBuilder.java,v $
   8  // $Author: derrickoswald $
   9  // $Date: 2005/04/12 11:27:42 $
  10  // $Revision: 1.5 $
  11  //
  12  // This library is free software; you can redistribute it and/or
  13  // modify it under the terms of the GNU Lesser General Public
  14  // License as published by the Free Software Foundation; either
  15  // version 2.1 of the License, or (at your option) any later version.
  16  //
  17  // This library is distributed in the hope that it will be useful,
  18  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  19  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20  // Lesser General Public License for more details.
  21  //
  22  // You should have received a copy of the GNU Lesser General Public
  23  // License along with this library; if not, write to the Free Software
  24  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  25  //
  26                                                                                                                                                  
  27  package org.htmlparser.parserapplications.filterbuilder;
  28  
  29  import java.awt.BorderLayout;
  30  import java.awt.Component;
  31  import java.awt.Container;
  32  import java.awt.Dimension;
  33  import java.awt.Event;
  34  import java.awt.FileDialog;
  35  import java.awt.FlowLayout;
  36  import java.awt.Insets;
  37  import java.awt.Point;
  38  import java.awt.Toolkit;
  39  import java.awt.datatransfer.Clipboard;
  40  import java.awt.datatransfer.ClipboardOwner;
  41  import java.awt.datatransfer.DataFlavor;
  42  import java.awt.datatransfer.StringSelection;
  43  import java.awt.datatransfer.Transferable;
  44  import java.awt.datatransfer.UnsupportedFlavorException;
  45  import java.awt.dnd.DnDConstants;
  46  import java.awt.dnd.DragGestureEvent;
  47  import java.awt.dnd.DragGestureListener;
  48  import java.awt.dnd.DragSource;
  49  import java.awt.dnd.DragSourceDragEvent;
  50  import java.awt.dnd.DragSourceDropEvent;
  51  import java.awt.dnd.DragSourceEvent;
  52  import java.awt.dnd.DragSourceListener;
  53  import java.awt.dnd.DropTarget;
  54  import java.awt.dnd.DropTargetContext;
  55  import java.awt.dnd.DropTargetDragEvent;
  56  import java.awt.dnd.DropTargetDropEvent;
  57  import java.awt.dnd.DropTargetEvent;
  58  import java.awt.dnd.DropTargetListener;
  59  import java.awt.event.ActionEvent;
  60  import java.awt.event.ActionListener;
  61  import java.awt.event.InputEvent;
  62  import java.awt.event.KeyEvent;
  63  import java.awt.event.MouseEvent;
  64  import java.awt.event.MouseListener;
  65  import java.awt.event.MouseMotionListener;
  66  import java.awt.event.WindowEvent;
  67  import java.awt.event.WindowListener;
  68  import java.beans.PropertyVetoException;
  69  import java.io.File;
  70  import java.io.FileReader;
  71  import java.io.FileWriter;
  72  import java.io.IOException;
  73  import java.io.LineNumberReader;
  74  import java.io.PrintWriter;
  75  import java.io.StringWriter;
  76  import java.lang.reflect.Method;
  77  import java.net.MalformedURLException;
  78  import java.net.URL;
  79  import java.util.Vector;
  80  
  81  import javax.swing.Icon;
  82  import javax.swing.ImageIcon;
  83  import javax.swing.JButton;
  84  import javax.swing.JDesktopPane;
  85  import javax.swing.JFrame;
  86  import javax.swing.JInternalFrame;
  87  import javax.swing.JMenu;
  88  import javax.swing.JMenuBar;
  89  import javax.swing.JMenuItem;
  90  import javax.swing.JOptionPane;
  91  import javax.swing.JPanel;
  92  import javax.swing.JPopupMenu;
  93  import javax.swing.JScrollPane;
  94  import javax.swing.JSeparator;
  95  import javax.swing.JSplitPane;
  96  import javax.swing.JTextField;
  97  //import javax.swing.JTextPane;
  98  import javax.swing.JToolBar;
  99  import javax.swing.JTree;
 100  import javax.swing.KeyStroke;
 101  import javax.swing.ScrollPaneConstants;
 102  import javax.swing.WindowConstants;
 103  
 104  import org.htmlparser.Parser;
 105  import org.htmlparser.beans.FilterBean;
 106  import org.htmlparser.parserapplications.filterbuilder.layouts.NullLayoutManager;
 107  import org.htmlparser.util.EncodingChangeException;
 108  import org.htmlparser.util.NodeIterator;
 109  import org.htmlparser.util.NodeList;
 110  import org.htmlparser.util.ParserException;
 111  
 112  /**
 113   * The main program for the FilterBuilder programming system.
 114   * <p>ToDo:
 115   * <ul>
 116   * <li>thread the attribute fetching</li>
 117   * <li>CSS selector filter</li>
 118   * <li>table row filter</li>
 119   * <li>table column filter</li>
 120   * <li>trigger filter</li>
 121   * <li>undo</li>
 122   * <li>handle bad URLs</li>
 123   * <li>StringBean type secondary text output</li>
 124   * <li>context sensitive menus</li>
 125   * </ul>
 126   */
 127  public class FilterBuilder
 128      extends
 129          JFrame
 130      implements
 131          WindowListener,
 132          ActionListener,
 133          MouseListener,
 134          MouseMotionListener,
 135          DragGestureListener,
 136          DragSourceListener,
 137          DropTargetListener,
 138          ClipboardOwner
 139  {
 140      static final String TITLE = "HTML Parser FilterBuilder";
 141  
 142      static final URL mDocumentBase;
 143      
 144      static
 145      {
 146          
 147          String p;
 148          char ps;
 149          URL base;
 150  
 151          p = System.getProperty ("user.dir");
 152          // if the system file separator isn't the URL file separator convert it.
 153          try
 154          {
 155              ps = (System.getProperty ("file.separator")).charAt(0);
 156              if ('/' != ps)
 157                  p.replace (ps, '/');
 158          }
 159          catch (StringIndexOutOfBoundsException e)
 160          {
 161          }
 162  
 163          try
 164          {
 165              base = new URL ("file:///" + p + "/");
 166          }
 167          catch (MalformedURLException murle)
 168          {
 169              base = null;
 170          }
 171          mDocumentBase = base;
 172      }
 173  
 174      static String mHomeDir;
 175      
 176      static
 177      {
 178          String dir;
 179          File file;
 180  
 181          dir = System.getProperty ("user.home")
 182              + System.getProperty ("file.separator")
 183              + ".htmlparser";
 184          file = new File (dir);
 185          if (!file.exists ())
 186              if (!file.mkdirs ()) // make the directory if it doesn't exist
 187                  throw new RuntimeException (
 188                      "cannot create directory "
 189                      + file.getAbsolutePath ());
 190          mHomeDir = file.getAbsolutePath ();
 191      }
 192      
 193      /**
 194       * The relative position of the mouse while dragging.
 195       */
 196      protected Point mBasePoint;
 197  
 198      /**
 199       * Selected commands.
 200       */
 201      protected Vector mSelection;
 202  
 203      /**
 204       * If true selection moved.
 205       */
 206      protected boolean mMoved;
 207  
 208      /**
 209       * This component is a drop target.
 210       */
 211      protected DropTarget mDropTarget;
 212      
 213      /**
 214       * Enables this component to be a Drag Source.
 215       */
 216      protected DragSource mDragSource;
 217  
 218      /**
 219       * Kludge: Used by actionPerformed/filterAction to remember the filter menu item.
 220       */
 221      protected Component mCurrentComponent;
 222  
 223      /**
 224       * The main panel GUI component.
 225       */
 226      protected JPanel mMainPanel;
 227  
 228      /**
 229       * The main panel scrolling GUI component.
 230       */
 231      protected JScrollPane mMainScroller;
 232  
 233      /**
 234       * The URL input GUI component.
 235       */
 236      protected JTextField mURLField;
 237  
 238      /**
 239       * The output panel GUI component.
 240       */
 241      protected JDesktopPane mOutput;
 242  
 243      /**
 244       * Create an FilterBuilder programming environment.
 245       */
 246      public FilterBuilder ()
 247      {
 248          JMenuBar menubar;
 249          JToolBar toolbar;
 250          JMenu menu;
 251          JPanel panel;
 252          JScrollPane pane;
 253          JSplitPane split;
 254          JMenuItem item;
 255  
 256          // drag and drop support
 257          mMainPanel = new JPanel ();
 258          mDropTarget = new DropTarget (mMainPanel, this);
 259          mDragSource = new DragSource ();
 260  
 261          // menu and toolbar
 262          menubar = new JMenuBar();
 263          toolbar = new JToolBar ();
 264          toolbar.setAlignmentY (0.222222F);
 265  
 266          // file menu
 267          menu = new JMenu ();
 268          menu.setText ("File");
 269          menu.setActionCommand ("File");
 270          menu.setMnemonic ((int)'F');
 271          makeMenuButton ("New", "Create a new document", "New", 'N', KeyStroke.getKeyStroke (KeyEvent.VK_N, Event.CTRL_MASK), toolbar, menu);
 272          makeMenuButton ("Open", "Open an existing document", "Open...", 'O', KeyStroke.getKeyStroke (KeyEvent.VK_O, Event.CTRL_MASK), toolbar, menu);
 273          makeMenuButton ("Save", "Save the active document", "Save...", 'S', KeyStroke.getKeyStroke (KeyEvent.VK_S, Event.CTRL_MASK), toolbar, menu);
 274          makeMenuButton ("SaveAs", "Save the active document", "Save As...", 'A', KeyStroke.getKeyStroke (KeyEvent.VK_A, Event.CTRL_MASK), null, menu);
 275          menu.add (new JSeparator ());
 276          makeMenuButton ("Exit", "Exit the program", "Exit", 'E', KeyStroke.getKeyStroke (KeyEvent.VK_E, Event.CTRL_MASK), null, menu);
 277          menubar.add (menu);
 278          
 279          toolbar.add(new JToolBar.Separator());
 280  
 281          // edit menu
 282          menu = new JMenu ();
 283          menu.setText ("Edit");
 284          menu.setActionCommand ("Edit");
 285          menu.setMnemonic ((int)'E');
 286          makeMenuButton ("Cut", "Cut the selection and put it on the Clipboard", "Cut", 'T', KeyStroke.getKeyStroke (KeyEvent.VK_X, Event.CTRL_MASK), toolbar, menu);
 287          makeMenuButton ("Copy", "Copy the selection and put it on the Clipboard", "Copy", 'C', KeyStroke.getKeyStroke (KeyEvent.VK_C, Event.CTRL_MASK), toolbar, menu);
 288          makeMenuButton ("Paste", "Insert Clipboard contents", "Paste", 'P', KeyStroke.getKeyStroke (KeyEvent.VK_V, Event.CTRL_MASK), toolbar, menu);
 289          makeMenuButton ("Delete", "Delete the selection", "Delete", 'D', KeyStroke.getKeyStroke (KeyEvent.VK_DELETE, 0), toolbar, menu);
 290          menubar.add (menu);
 291  
 292          // filter menu
 293          menu = new JMenu ();
 294          menu.setText ("Filter");
 295          menu.setActionCommand ("Filter");
 296          menu.setMnemonic ((int)'F');
 297          menubar.add (menu);
 298  
 299          toolbar.add (new JToolBar.Separator());
 300  
 301          // filters menu and filters toolbar
 302          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.AndFilterWrapper");
 303          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.OrFilterWrapper");
 304          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.NotFilterWrapper");
 305          menu.addSeparator ();
 306          toolbar.add (new JToolBar.Separator ());
 307  
 308          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.StringFilterWrapper");
 309          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.RegexFilterWrapper");
 310          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.TagNameFilterWrapper");
 311          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.NodeClassFilterWrapper");
 312          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasAttributeFilterWrapper");
 313          menu.addSeparator ();
 314          toolbar.add (new JToolBar.Separator ());
 315  
 316          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasParentFilterWrapper");
 317          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasChildFilterWrapper");
 318          addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasSiblingFilterWrapper");
 319          menu.addSeparator ();
 320          toolbar.add (new JToolBar.Separator ());
 321  
 322          // operation menu
 323          menu = new JMenu ();
 324          menu.setText ("Operation");
 325          menu.setActionCommand ("Operation");
 326          menu.setMnemonic ((int)'r');
 327          item = new JMenuItem ();
 328          item.setText ("Expand");
 329          item.setActionCommand ("expandAction");
 330          item.addActionListener (this);
 331          menu.add (item);
 332          item = new JMenuItem ();
 333          item.setText ("Collapse");
 334          item.setActionCommand ("collapseAction");
 335          item.addActionListener (this);
 336          menu.add (item);
 337          menu.addSeparator ();
 338          item = new JMenuItem ();
 339          item.setText ("Expand All");
 340          item.setActionCommand ("expandAllAction");
 341          item.addActionListener (this);
 342          menu.add (item);
 343          item = new JMenuItem ();
 344          item.setText ("Collapse All");
 345          item.setActionCommand ("collapseAllAction");
 346          item.addActionListener (this);
 347          menu.add (item);
 348          menu.addSeparator ();
 349          item = new JMenuItem ("Fetch Page");
 350          item.setActionCommand ("fetchAction");
 351          item.addActionListener (this);
 352          menu.add (item);
 353          item = new JMenuItem ("Execute Filter");
 354          item.setActionCommand ("executeAction");
 355          item.addActionListener (this);
 356          menu.add (item);
 357          menubar.add (menu);
 358  
 359          // help menu
 360          menu = new JMenu ();
 361          menu.setText ("Help");
 362          menu.setActionCommand ("Help");
 363          menu.setMnemonic ((int)'H');
 364          item = new JMenuItem ("Filtering");
 365          item.setActionCommand ("filteringAction");
 366          item.addActionListener (this);
 367          menu.add (item);
 368          item = new JMenuItem ("Instructions");
 369          item.setActionCommand ("instructionsAction");
 370          item.addActionListener (this);
 371          menu.add (item);
 372          item = new JMenuItem ("Tutorial");
 373          item.setActionCommand ("tutorialAction");
 374          item.addActionListener (this);
 375          menu.add (item);
 376          item = new JMenuItem ("Hints");
 377          item.setActionCommand ("hintsAction");
 378          item.addActionListener (this);
 379          menu.add (item);
 380          makeMenuButton ("About", "Display program information, version number and copyright", "About", 'B', KeyStroke.getKeyStroke (KeyEvent.VK_H, Event.CTRL_MASK), toolbar, menu);
 381          menubar.add (menu);
 382  
 383          setJMenuBar (menubar);
 384  
 385          // toolbar panel
 386          panel = new JPanel ();
 387          panel.setLayout (new FlowLayout (FlowLayout.LEFT,0,0));
 388          panel.add (toolbar);
 389          getContentPane().setLayout (new BorderLayout (0,0));
 390          getContentPane ().add (BorderLayout.NORTH, panel);
 391  
 392          // URL entry
 393          mURLField = new JTextField ();
 394          mURLField.setToolTipText ("Enter the URL to view");
 395  //        mTextField.addActionListener (this);
 396          mURLField.setText ("http://sourceforge.org/projects/htmlparser");
 397          getContentPane().add (BorderLayout.SOUTH, mURLField);
 398  
 399          // application setup
 400          setTitle (TITLE);
 401          setDefaultCloseOperation (WindowConstants.DO_NOTHING_ON_CLOSE);
 402          setSize (640, 480);
 403          setVisible (false);
 404  
 405          // main panel
 406          mMainPanel.setLayout (new NullLayoutManager ());
 407          mMainScroller = new JScrollPane (
 408                  mMainPanel,
 409                  ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
 410                  ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 411  
 412          split = new JSplitPane ();
 413          pane = new JScrollPane ();
 414          pane.setViewportView (mMainScroller);
 415          split.setLeftComponent (pane);
 416  
 417          mOutput = new JDesktopPane ();
 418          split.setRightComponent (mOutput);
 419  
 420          getContentPane().add (BorderLayout.CENTER, split);
 421  
 422          // shenanigans to get the splitter bar at the midpoint
 423          setVisible (true);
 424          split.setDividerLocation (0.5);
 425          setVisible (false);
 426  
 427          // listeners
 428          addWindowListener (this);
 429          setIconImage (Toolkit.getDefaultToolkit ().getImage ("images/program16.gif"));
 430          addMouseListener (this);
 431          addMouseMotionListener (this);
 432  
 433          // clipboard buffer
 434          mSelection = new Vector ();
 435      }
 436  
 437      /**
 438       * Creates a new instance of an FilterBuilder environment with the given title.
 439       * @param title the title for the new frame.
 440       * @see #FilterBuilder()
 441       */
 442      public FilterBuilder (String title)
 443      {
 444          this ();
 445          setTitle (title);
 446      }
 447  
 448      /**
 449       * Makes menu and toolbar items for commands.
 450       * @param name The name of the command.
 451       * @param description A description for the tooltip.
 452       * @param text The text for the menu.
 453       * @param mnemonic The navigation mnemonic.
 454       * @param key Accelerator key.
 455       * @param toolbar The toolbar to add the button to.
 456       * @param menu The menu to add the menu item to.
 457       */
 458      protected void makeMenuButton (
 459              String name,
 460              String description,
 461              String text,
 462              int mnemonic,
 463              KeyStroke key,
 464              JToolBar toolbar,
 465              JMenu menu)
 466      {
 467          JButton button;
 468          JMenuItem item;
 469          ImageIcon icon;
 470          String command;
 471  
 472          command = name.toLowerCase ();
 473          try
 474          {
 475              icon = new ImageIcon (getURL ("images/" + command + ".gif"));
 476          }
 477          catch (java.net.MalformedURLException error)
 478          {
 479              icon = null;
 480          }
 481  
 482          item = new JMenuItem ();
 483          item.setText (text);
 484          item.setActionCommand (command + "Action");
 485          item.setAccelerator (key);
 486          item.setMnemonic (mnemonic);
 487          item.setIcon (icon);
 488          item.addActionListener (this);
 489          menu.add (item);
 490  
 491          if (null != toolbar)
 492          {
 493              button = new JButton ();
 494              button.setDefaultCapable (false);
 495              button.setToolTipText (description);
 496              button.setMnemonic (mnemonic);
 497              button.setActionCommand (command + "Action");
 498              button.setMargin (new Insets (0, 0, 0, 0));
 499              button.setIcon (icon);
 500              button.addActionListener (this);
 501              toolbar.add (button);
 502          }
 503      }
 504  
 505      /**
 506       * Get a url for the given resource specification.
 507       * @param spec The name of the resource.
 508       * @return The fully formed URL.
 509       * @exception MalformedURLException In the case that the document base
 510       * or name of the resource cannot be turned into a URL.
 511       */
 512      protected URL getURL (String spec)
 513          throws MalformedURLException
 514      {
 515          URL ret;
 516  
 517          if (null == (ret = getClass ().getResource (spec)))
 518              if ((null != mDocumentBase) && (-1 == spec.indexOf ("//")))
 519                  ret =  new URL (mDocumentBase, spec);
 520              else
 521                  ret = new URL (spec);
 522  
 523          return ret;
 524      }
 525  
 526      /**
 527       * Creates a new button for the given class.
 528       * @param class_name The name of the Filter class.
 529       * @return A fully functional button with name, tool tip,
 530       * icon and drag recognizer.
 531       */
 532      public JButton makeFilterButton (String class_name)
 533      {
 534          Filter filter;
 535          JButton ret;
 536  
 537          ret = new JButton ();
 538          filter = Filter.instantiate (class_name);
 539          if (null != filter)
 540          {
 541              ret.setName (class_name); // filter.getNodeFilter ().getClass ().getName ());
 542              ret.setToolTipText (filter.getDescription ());
 543              ret.setMargin (new Insets (0, 0, 0, 0));
 544              ret.setIcon (filter.getIcon ());
 545              mDragSource.createDefaultDragGestureRecognizer (
 546                  ret,
 547                  DnDConstants.ACTION_MOVE,
 548                  this);
 549              ret.setActionCommand ("filterAction");
 550              ret.addActionListener (this);
 551          }
 552          
 553          return (ret);
 554      }
 555  
 556      /**
 557       * Add a filter to the GUI.
 558       * Adds the filter specified by class_name to the menu, toolbar and
 559       * starts listening for actions.
 560       * @param menu The menu to add the filter to.
 561       * @param toolbar The toolbar to add the filter to.
 562       * @param class_name The class name for the filter wrapper.
 563       * From the wrapper, the NodeFilter, description and icon can be obtained.
 564       */
 565      public void addFilter (JMenu menu, JToolBar toolbar, String class_name)
 566      {
 567          Filter filter;
 568  
 569          filter = Filter.instantiate (class_name);
 570          if (null != filter)
 571          {
 572              String name;
 573              String description;
 574              Icon icon;
 575              String text;
 576              JMenuItem item;
 577  
 578              name = filter.getNodeFilter ().getClass ().getName ();
 579              description = filter.getDescription ();
 580              icon = filter.getIcon ();
 581              text = name.substring (name.lastIndexOf ('.') + 1);
 582  
 583              item = new JMenuItem ();
 584              item.setName (class_name);
 585              item.setText (text);
 586              item.setActionCommand ("filterAction");
 587  //            item.setAccelerator (key);
 588  //            item.setMnemonic (mnemonic);
 589              item.setToolTipText (description);
 590              item.setIcon (icon);
 591              item.addActionListener (this);
 592              menu.add (item);
 593              
 594              toolbar.add (makeFilterButton (class_name));
 595          }
 596      }
 597  
 598      /**
 599       * Adds a set of filters to the main panel or a sublist.
 600       * Sets up the GUI components as drop targets and mouse listeners,
 601       * and performs a relayout to display them.
 602       * @param filters The filter wrappers to add.
 603       * @param point The point at which to start adding (list == null).
 604       * @param list The list to add to (point not used), or <code>null</code>
 605       * for the main panel.
 606       */
 607      protected void insertFilters (Filter[] filters, Point point, SubFilterList list)
 608      {
 609          Dimension dimension;
 610  
 611          if (null == list)
 612          {
 613              for (int i = 0; i < filters.length; i++)
 614              {
 615  	            filters[i].setLocation (point);
 616  	            mMainPanel.add (filters[i]);
 617  	            dimension = filters[i].getPreferredSize ();
 618  	            point.y += dimension.height;
 619              }
 620          }
 621          else
 622              for (int i = 0; i < filters.length; i++)
 623                  list.addFilter (filters[i]);
 624          setupDropTargets (filters);
 625          setupMouseListeners (filters);
 626          relayout ();
 627      }
 628  
 629      /**
 630       * Sets the position of the mouse in the component.
 631       * 
 632       * @param point The point where the mouse position is.
 633       */
 634      protected void setBasePoint (Point point)
 635      {
 636          mBasePoint = point;
 637      }
 638      
 639      /**
 640       * Gets the current base point of the mouse pointer.
 641       * This value is used to offset the drag position
 642       * to maintain the mouse position at the same
 643       * relative position within the card while dragging.
 644       * 
 645       * @return The current base point of the mouse pointer.
 646       */
 647      protected Point getBasePoint ()
 648      {
 649          return (mBasePoint);
 650      }
 651  
 652      /**
 653       * Get the enclosing sub filter list if any.
 654       * @param component The component that's supposedly enclosed.
 655       * @return The enclosing component or <code>null</code> otherwise.
 656       */
 657      protected SubFilterList getEnclosing (Component component)
 658      {
 659          do
 660              component = component.getParent ();
 661          while (     (null != component)
 662                  && !(component instanceof SubFilterList));
 663  
 664          return ((SubFilterList)component);
 665      }
 666  
 667      /**
 668       * Get the enclosed sub filter list if any.
 669       * @param component The component that's supposedly enclosing the list.
 670       * @return The enclosed component or <code>null</code> otherwise.
 671       */
 672      protected SubFilterList getEnclosed (Component component)
 673      {
 674          Component[] list;
 675  
 676          if (component instanceof Container)
 677          {
 678              list = ((Container)component).getComponents  ();
 679              for (int i = 0; i < list.length; i++)
 680                  if (list[i] instanceof SubFilterList)
 681                      return ((SubFilterList)list[i]);
 682          }
 683  
 684          return (null);
 685      }
 686  
 687  
 688      /**
 689       * Makes a program like:
 690       * <pre>
 691       * // Generated by FilterBuilder. http://htmlparser.org
 692       * // [aced0005737200206f72672e68746d6c7061727365722e66696c746572732e416e6446696c74657224c30516b2b7b2120200015b000b6d5072656469636174657374001c5b4c6f72672f68746d6c7061727365722f4e6f646546696c7465723b78707572001c5b4c6f72672e68746d6c7061727365722e4e6f646546696c7465723b8f17479b1d5f7992020000787000000002737200246f72672e68746d6c7061727365722e66696c746572732e5461674e616d6546696c746572b28b2601a614890f0200014c00056d4e616d657400124c6a6176612f6c616e672f537472696e673b78707400044d455441737200296f72672e68746d6c7061727365722e66696c746572732e48617341747472696275746546696c74657296abdfb3b0714cda0200024c000a6d41747472696275746571007e00064c00066d56616c756571007e000678707400046e616d6570]
 693       *                                                                                                                                                         
 694       * import org.htmlparser.*;
 695       * import org.htmlparser.filters.*;
 696       * import org.htmlparser.beans.*;
 697       * import org.htmlparser.util.*;
 698       *                                                                                                                                                         
 699       * public class Test
 700       * {
 701       *     public static void main (String args[])
 702       *     {
 703       *         TagNameFilter filter0 = new TagNameFilter ();
 704       *         filter0.setName ("META");
 705       *         HasAttributeFilter filter1 = new HasAttributeFilter ();
 706       *         filter1.setAttributeName ("name");
 707       *         NodeFilter[] array0 = new NodeFilter[2];
 708       *         array0[0] = filter0;
 709       *         array0[1] = filter1;
 710       *         AndFilter filter2 = new AndFilter ();
 711       *         filter2.setPredicates (array0);
 712       *         NodeFilter[] array1 = new NodeFilter[1];
 713       *         array1[0] = filter2;
 714       *         FilterBean bean = new FilterBean ();
 715       *         bean.setFilters (array1);
 716       *         if (0 != args.length)
 717       *         {
 718       *             bean.setURL (args[0]);
 719       *             System.out.println (bean.getNodes ().toHtml ());
 720       *         }
 721       *         else
 722       *             System.out.println ("Usage: java -classpath .:htmlparser.jar Test <url>");
 723       *     }
 724       * }
 725       * </pre>
 726       * @param name The name of the class. 
 727       * @param out The buffer to append to.
 728       * @param bean The bean to extract the filters from to make the program.
 729       */
 730      protected void makeProgram (String name, StringBuffer out, FilterBean bean)
 731      {
 732          // so we need to keep track of filters and arrays of filters to give them unique numbers
 733          // each Filter is responsible for outputting it's code and returning it's variable name
 734          int[] context; // 0 - indent, 1 - next filter variable #, 2 - next array of filters variable #
 735          String[] names;
 736          Filter[] filters;
 737          String array;
 738  
 739          filters = (Filter[])bean.getFilters ();
 740  
 741          context = new int[3];
 742          context[0] = 0;
 743  
 744          Filter.spaces (out, context[0]);
 745          out.append ("// Generated by FilterBuilder. http://htmlparser.org");
 746          Filter.newline (out);
 747          Filter.spaces (out, context[0]);
 748          out.append ("// ");
 749          try
 750          {
 751              out.append (Filter.deconstitute (filters));
 752          }
 753          catch (IOException ioe)
 754          {
 755              ioe.printStackTrace ();
 756          }
 757          Filter.newline (out);
 758          Filter.newline (out);
 759  
 760          Filter.spaces (out, context[0]);
 761          out.append ("import org.htmlparser.*;");
 762          Filter.newline (out);
 763          Filter.spaces (out, context[0]);
 764          out.append ("import org.htmlparser.filters.*;");
 765          Filter.newline (out);
 766          Filter.spaces (out, context[0]);
 767          out.append ("import org.htmlparser.beans.*;");
 768          Filter.newline (out);
 769          Filter.spaces (out, context[0]);
 770          out.append ("import org.htmlparser.util.*;");
 771          Filter.newline (out);
 772          Filter.newline (out);
 773  
 774          Filter.spaces (out, context[0]);
 775          out.append ("public class ");
 776          out.append (name);
 777          Filter.newline (out);
 778          Filter.spaces (out, context[0]);
 779          out.append ("{");
 780  
 781          context[0] = 4;
 782          Filter.newline (out);
 783          Filter.spaces (out, context[0]);
 784          out.append ("public static void main (String args[])");
 785          Filter.newline (out);
 786          Filter.spaces (out, context[0]);
 787          out.append ("{");
 788          Filter.newline (out);
 789          
 790          context[0] = 8;
 791          names = new String [filters.length];
 792          for (int i = 0; i < names.length; i++)
 793              names[i] = filters[i].toJavaCode (out, context);
 794  
 795          array = "array" + context[2]++;
 796          Filter.spaces (out, context[0]);
 797          out.append ("NodeFilter[] ");
 798          out.append (array);
 799          out.append (" = new NodeFilter[");
 800          out.append (filters.length);
 801          out.append ("];");
 802          Filter.newline (out);
 803          for (int i = 0; i < filters.length; i++)
 804          {
 805              Filter.spaces (out, context[0]);
 806  	        out.append (array);
 807  	        out.append ("[");
 808  	        out.append (i);
 809  	        out.append ("] = ");
 810  	        out.append (names[i]);
 811  	        out.append (";");
 812  	        Filter.newline (out);
 813          }
 814  
 815          Filter.spaces (out, context[0]);
 816          out.append ("FilterBean bean = new FilterBean ();");
 817          Filter.newline (out);
 818          Filter.spaces (out, context[0]);
 819          out.append ("bean.setFilters (");
 820          out.append (array);
 821          out.append (");");
 822          Filter.newline (out);
 823          Filter.spaces (out, context[0]);
 824          out.append ("if (0 != args.length)");
 825          Filter.newline (out);
 826          Filter.spaces (out, context[0]);
 827          out.append ("{");
 828          Filter.newline (out);
 829          context[0] = 12;
 830          Filter.spaces (out, context[0]);
 831          out.append ("bean.setURL (args[0]);");
 832          Filter.newline (out);
 833          Filter.spaces (out, context[0]);
 834          out.append ("System.out.println (bean.getNodes ().toHtml ());");
 835          Filter.newline (out);
 836          context[0] = 8;
 837          Filter.spaces (out, context[0]);
 838          out.append ("}");
 839          Filter.newline (out);
 840          Filter.spaces (out, context[0]);
 841          out.append ("else");
 842          Filter.newline (out);
 843          context[0] = 12;
 844          Filter.spaces (out, context[0]);
 845          out.append ("System.out.println (\"Usage: java -classpath .:htmlparser.jar ");
 846          out.append (name);
 847          out.append (" <url>\");");
 848          Filter.newline (out);
 849          
 850          context[0] = 4;
 851          Filter.spaces (out, context[0]);
 852          out.append ("}");
 853          Filter.newline (out);
 854  
 855          context[0] = 0;
 856          Filter.spaces (out, context[0]);
 857          out.append ("}");
 858          Filter.newline (out);
 859      }
 860  
 861      /**
 862       * Extracts a java class name from a file name.
 863       * ToDo: make this package-smart somehow.
 864       * @param file The name of the file.
 865       * @return The name of the class.
 866       */
 867      protected String classFromFile (String file)
 868      {
 869          String filesep;
 870          int index;
 871  
 872          // remove any path
 873          filesep = System.getProperty ("file.separator");
 874          index = file.lastIndexOf (filesep);
 875          if (-1 != index)
 876              file = file.substring (index + filesep.length ());
 877          // remove the extension
 878          index = file.indexOf ('.');
 879          if (-1 != index)
 880              file = file.substring (0, index);
 881  
 882          return (file);
 883      }
 884  
 885      /**
 886       * Save the workspace contents to file.
 887       * @param name The name of the file to save it under.
 888       */
 889      public void save (String name)
 890      {
 891          Filter[] selections;
 892          FilterBean bean;
 893          StringBuffer buffer;
 894          PrintWriter out;
 895          String ok = "OK";
 896  
 897          selections = getFilters ();
 898          if (0 != selections.length)
 899          {
 900              bean = new FilterBean ();
 901              bean.setURL (mURLField.getText ());
 902              bean.setFilters (selections);
 903              buffer = new StringBuffer ();
 904              makeProgram (classFromFile (name), buffer, bean);
 905              try
 906              {
 907                  out = new PrintWriter (new FileWriter (name), true);
 908                  try
 909                  {
 910                      out.write (buffer.toString ());
 911                      out.flush ();
 912                  }
 913                  finally
 914                  {
 915                      out.close ();
 916                  }
 917              }
 918              catch (IOException ioe)
 919              {
 920                  ioe.printStackTrace ();
 921              }
 922          }
 923          else // ToDo: grey out save option if nothing to save...
 924              JOptionPane.showOptionDialog (
 925                  mMainPanel,
 926                  "No filters to save.",
 927                  "Oops",
 928                  JOptionPane.DEFAULT_OPTION,
 929                  JOptionPane.ERROR_MESSAGE,
 930                  null,
 931                  new String[] { ok },
 932                  ok);
 933      }
 934  
 935      /**
 936       * The action to take when "New" menu or button pressed.
 937       */
 938      protected void newAction ()
 939      {
 940          mMainPanel.removeAll ();
 941          mSelection.clear ();
 942          relayout ();
 943      }
 944  
 945      /**
 946       * The action to take when "Open" menu or button pressed.
 947       */
 948      protected void openAction ()
 949      {
 950          FileDialog dialog;
 951          File file;
 952  
 953          dialog = new FileDialog (this);
 954          dialog.setMode (FileDialog.LOAD);
 955          dialog.setTitle ("Open");
 956          dialog.setDirectory (mHomeDir);
 957          dialog.setVisible (true);
 958          if (null != dialog.getFile ())
 959          {
 960              mHomeDir = dialog.getDirectory ();
 961              file = new File (mHomeDir + dialog.getFile ());
 962              open (file.getAbsolutePath ());
 963              setTitle (TITLE + " - " + file.getAbsolutePath ());
 964          }
 965      }
 966  
 967      /**
 968       * The action to take when "Save" menu or button pressed.
 969       */
 970      protected void saveAction ()
 971      {
 972          String title;
 973          int index;
 974          File file;
 975          FileDialog dialog;
 976  
 977          title = getTitle ();
 978          index = title.indexOf (" - ");
 979          if (-1 != index)
 980              file = new File (title.substring (index + 3));
 981          else
 982          {
 983              dialog = new FileDialog (this);
 984              dialog.setMode (FileDialog.SAVE);
 985              dialog.setTitle ("Save");
 986              dialog.setDirectory (mHomeDir);        
 987              dialog.setVisible (true);
 988  	        if (null != dialog.getFile ())
 989  	        {
 990  	            mHomeDir = dialog.getDirectory ();
 991  	            file = new File (mHomeDir + dialog.getFile ());
 992  	            setTitle (TITLE + " - " + file.getAbsolutePath ());
 993  	        }
 994  	        else
 995  	            file = null;
 996          }
 997          if (null != file)
 998              save (file.getAbsolutePath ());
 999      }
1000  
1001      /**
1002       * The action to take when "Save As" menu or button pressed.
1003       */
1004      protected void saveasAction ()
1005      {
1006          setTitle (TITLE);
1007          saveAction ();
1008      }
1009  
1010      /**
1011       * The action to take when "Exit" menu or button pressed.
1012       */
1013      protected void exitAction ()
1014      {
1015          exitApplication ();
1016      }
1017  
1018      /**
1019       * The action to take when "Cut" menu or button pressed.
1020       */
1021      protected void cutAction ()
1022      {
1023          String string;
1024          StringSelection contents;
1025          Clipboard cb;
1026              
1027          // get the selection
1028          string = serializeSelection ();
1029          // copy to clipboard
1030          contents = new StringSelection (string);
1031          cb = Toolkit.getDefaultToolkit ().getSystemClipboard ();
1032          cb.setContents (contents, this);
1033          // delete the selection
1034          deleteSelection ();
1035          relayout ();
1036      }
1037  
1038      /**
1039       * The action to take when "Copy" menu or button pressed.
1040       */
1041      protected void copyAction ()
1042      {
1043          String string;
1044          StringSelection contents;
1045          Clipboard cb;
1046              
1047          // get the selection
1048          string = serializeSelection ();
1049          // copy to clipboard
1050          contents = new StringSelection (string);
1051          cb = Toolkit.getDefaultToolkit ().getSystemClipboard ();
1052          cb.setContents (contents, this);
1053      }
1054  
1055      /**
1056       * The action to take when "Paste" menu or button pressed.
1057       */
1058      protected void pasteAction ()
1059      {
1060          Clipboard cb;
1061          Transferable content;
1062          String string;
1063          Filter[] filters;
1064          Point point;
1065          SubFilterList list;
1066              
1067          // get the text
1068          cb = Toolkit.getDefaultToolkit ().getSystemClipboard ();
1069          content = cb.getContents (this);
1070          if (content.isDataFlavorSupported (DataFlavor.stringFlavor))
1071          {
1072              try
1073              {
1074                  string = (String)content.getTransferData (DataFlavor.stringFlavor);
1075                  // deserialize it and add into the selection
1076                  filters = Filter.reconstitute (string, new Parser (mURLField.getText ()));
1077                  // add it to the (single) selected object or main panel
1078                  if (isSingleSelection ()
1079                          && (null != (list = getEnclosed (getSelection ()[0]))))
1080                  {
1081                      for (int i = 0; i < filters.length; i++)
1082                          list.addFilter (filters[i]);
1083                  }
1084                  else
1085                  {
1086                      point = new Point (0,0);
1087                      for (int i = 0; i < filters.length; i++)
1088                      {
1089                          filters[i].setLocation (point);
1090                          mMainPanel.add (filters[i]);
1091                          point.y += filters[i].getPreferredSize ().height;
1092                      }
1093                  }
1094                  setupMouseListeners (filters);
1095                  setupDropTargets (filters);
1096                  relayout ();
1097              }
1098              catch (Exception e)
1099              {
1100                  e.printStackTrace ();
1101              }
1102          }
1103      }
1104  
1105      /**
1106       * The action to take when "Delete" menu or button pressed.
1107       */
1108      protected void deleteAction ()
1109      {
1110          // delete the selection
1111          deleteSelection ();
1112          relayout ();
1113      }
1114  
1115      /**
1116       * The action to take when a filter menu or button pressed.
1117       */
1118      protected void filterAction ()
1119      {
1120          String cls;
1121          Filter filter;
1122          SubFilterList list;
1123          Point point;
1124          
1125          // retrieve the source component placed there by actionPerformed
1126          cls = mCurrentComponent.getName ();
1127          filter = Filter.instantiate (cls);
1128          // need this to get the underlying filter prepped?
1129          try
1130          {
1131              filter = Filter.wrap (filter.getNodeFilter (), new Parser (mURLField.getText ()));
1132          }
1133          catch (ParserException pe)
1134          {
1135              pe.printStackTrace ();
1136          }
1137          // add it to the (single) selected object or main panel
1138          if (isSingleSelection ()
1139                  && (null != (list = getEnclosed (getSelection ()[0]))))
1140          {
1141              insertFilters (new Filter[] {filter}, null, list);
1142          }
1143          else
1144          {
1145              point = new Point (50,50); // find where and who to stick it into
1146              insertFilters (new Filter[] {filter}, point, null);
1147          }
1148      }
1149      
1150      /**
1151       * The action to take when "Fetch" menu pressed.
1152       */
1153      protected void fetchAction ()
1154      {
1155          JInternalFrame frame;
1156          Dimension dimension;
1157          Parser parser;
1158          NodeList list;
1159  
1160          // set up an internal frame for the results
1161          frame = new JInternalFrame (mURLField.getText ()); 
1162          frame.setClosable (true); 
1163          frame.setResizable (true);
1164          dimension = mOutput.getSize ();
1165          frame.setBounds (0, 0, dimension.width, dimension.height);
1166          list = new NodeList ();
1167          try
1168          {
1169  	        parser = new Parser (mURLField.getText ());
1170  	        try
1171  	        {
1172  		        for (NodeIterator iterator = parser.elements (); iterator.hasMoreNodes (); )
1173  		            list.add (iterator.nextNode ());
1174  	        }
1175  	        catch (EncodingChangeException ece)
1176  	        {
1177  	            list.removeAll ();
1178  	            parser.reset ();
1179  		        for (NodeIterator iterator = parser.elements (); iterator.hasMoreNodes (); )
1180  		            list.add (iterator.nextNode ());
1181  	        }
1182          }
1183          catch (ParserException pe)
1184          {
1185              pe.printStackTrace ();
1186          }
1187          JTree tree = new JTree (new HtmlTreeModel (list));
1188          tree.setRootVisible (false);
1189          tree.setCellRenderer (new HtmlTreeCellRenderer ());
1190          JScrollPane treeView = new JScrollPane (tree);
1191  		frame.setContentPane (new JScrollPane (
1192  		    treeView,
1193  		    ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
1194  		    ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
1195          mOutput.add (frame, new Integer (1));   
1196          try
1197          {
1198              frame.setSelected (true); 
1199          }
1200          catch (PropertyVetoException pve)
1201          {
1202              pve.printStackTrace ();
1203          }
1204          frame.show (); 
1205      }
1206  
1207      /**
1208       * The action to take when "Execute" menu or button pressed.
1209       */
1210      protected void executeAction ()
1211      {
1212          Filter[] selections;
1213          FilterBean bean;
1214          JInternalFrame frame;
1215          Dimension dimension;
1216  //        JTextPane text;
1217  
1218          selections = getSelection ();
1219          if (0 == selections.length)
1220              selections = getFilters ();
1221          if (0 != selections.length)
1222          {
1223              bean = new FilterBean ();
1224              bean.setURL (mURLField.getText ());
1225              bean.setFilters (selections);
1226              
1227              // set up an internal frame for the results
1228              frame = new JInternalFrame (bean.getURL ()); 
1229              frame.setClosable (true); 
1230              frame.setResizable (true);
1231              dimension = mOutput.getSize ();
1232              frame.setBounds (0, 0, dimension.width, dimension.height / 2);
1233              JTree tree = new JTree (new HtmlTreeModel (bean.getNodes ()));
1234              tree.setRootVisible (false);
1235              tree.setCellRenderer (new HtmlTreeCellRenderer ());
1236              JScrollPane treeView = new JScrollPane (tree);
1237  			frame.setContentPane (new JScrollPane (
1238  			    treeView,
1239  			    ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
1240  			    ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
1241  //            text = new JTextPane ();
1242  //            text.setText (bean.getNodes ().toHtml ());
1243  //            frame.setContentPane (new JScrollPane (
1244  //                    text,
1245  //                    ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
1246  //                    ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
1247              mOutput.add (frame, new Integer(2)); // layer 2?   
1248              try
1249              {
1250                  frame.setSelected (true); 
1251              }
1252              catch (PropertyVetoException pve)
1253              {
1254                  pve.printStackTrace ();
1255              }
1256              frame.show (); 
1257          }
1258      }
1259  
1260      /**
1261       * The action to take when "Instructions" menu pressed.
1262       */
1263      protected void instructionsAction ()
1264      {
1265          String instructions =
1266              "<html>" +
1267              "Enter the target URL in the text box at the bottom of the window.<br>" +
1268              "Choose 'Fetch Page' from the Operations menu to see the whole page.<br>" +
1269              "Pick filters from the Filter menu or drag them from the toolbar.<br>" +
1270              "Filters such as And, Or, Not, HasParent, HasChild and HasSibling contain other filters:<br>" +
1271              "<ul><li>drag new filters into their blank areas at the bottom</li>" +
1272              "<li>cut an existing filter and paste into a selected filter</li></ul>" +
1273              "Build the filter incrementally, choosing 'Execute Filter' to test the selected filter.<br>" +
1274              "Save creates a .java file that runs the top level filter.<br>" +
1275              "Right click on a filter displays a pop-up menu.<br>" +
1276              "Double click on a blue item in the result pane expands the tree." +
1277              "</html>";
1278          String close = "Close";
1279          JOptionPane.showOptionDialog (
1280          // not .showMessageDialog(
1281              mMainPanel,
1282              instructions,
1283              "FilterBuilder Instructons",
1284              // remove this:
1285              JOptionPane.DEFAULT_OPTION,
1286              JOptionPane.INFORMATION_MESSAGE,
1287              // and remove rest of these:
1288              null,
1289              new String[] { close },
1290              close);
1291      }
1292  
1293      /**
1294       * The action to take when "Filtering" menu pressed.
1295       */
1296      protected void filteringAction ()
1297      {
1298          String instructions =
1299              "<html>" +
1300              "The HTML Parser filter subsystem extracts items from a web page,<br>" +
1301              "corresponding to the use-case 'I want this little piece of information from http://yadda'.<br>" +
1302              "The web page is considered a heirarchical tree of nodes. Usually the root node is &lt;html&gt;,<br>" +
1303              "intermediate level nodes are &lt;div&gt; and &lt;table&gt; for example,<br>" +
1304              "and leaf nodes are things like text or &lt;img&gt;.<br>" +
1305              "Any node that isn't the root node has a 'parent' node.<br>" +
1306              "Leaf nodes, by definition, have no 'children'.<br>" +
1307              "A filter is a Java class that answers the simple question:<br>" +
1308              "<pre>Is this node acceptable? True or false.</pre><br>" +
1309              "Some filters know the answer just by looking at the node,<br>" +
1310              "while others must ask other filters, sometimes looking up or down the node heirarchy.<br>" +
1311              "<b>The FilterBuilder is a program for making other programs that use filters.</b><br>" +
1312              "By combining different types of filters, specific nodes can be isolated from the<br>" +
1313              "target web page.<br>" +
1314              "The results are usually passed on to another part of the users program<br>" +
1315              "that does something useful with them.<br>" +
1316              "The filters available include:<br>" +
1317              "<ul>" +
1318              "<li>AndFilter - The main 'combining' filter, answers <code>true</code> only if<br>" +
1319              "all it's subfilters (predicates) are <code>true</code>.</li>" +
1320              "<li>OrFilter - A 'combining' filter that answers <code>true</code> if<br>" +
1321              "any of it's subfilters (predicates) are <code>true</code>.</li>" +
1322              "<li>NotFilter - A 'reversing' filter that answers <code>true</code> if<br>" +
1323              "it's subfilter (predicate) is <code>false</code>.</li>" +
1324              "<li>StringFilter - A 'leaf' filter that answers <code>true</code> if<br>" +
1325              "the node is text and it contains a certain sequence of characters.<br>" +
1326              "It can be made case insensitive, but in this case a 'locale' must be<br>" +
1327              "supplied as a context for upper-case conversion.</li>" +
1328              "<li>RegexFilter - A 'leaf' filter that answers <code>true</code> if<br>" +
1329              "the node is text and it contains a certain pattern (regular expression).<br>" +
1330              "Regular expressions are descibed in the java.util.regex.Pattern class documentation.</li>" +
1331              "<li>TagNameFilter - A filter that answers <code>true</code> if<br>" +
1332              "the node is a tag and it has a certain name," +
1333              "i.e. &lt;div&gt; would match the name <code>DIV</code>.</li>" +
1334              "<li>NodeClassFilter - A filter that answers <code>true</code> if<br>" +
1335              "the node is a certain tag class. Not recommended, use TagNameFilter instead.</li>" +
1336              "<li>HasAttributeFilter - A filter that answers <code>true</code> if<br>" +
1337              "the node is a tag and it has a certain attribute,<br>" +
1338              "i.e. &lt;script language=javascript&gt; would match the attribute <code>LANGUAGE</code>.<br>" +
1339              "It can be further restricted to have a certain attribute value as well,<br>" +
1340              "i.e. 'javascript' in this example.</li>" +
1341              "<li>HasParentFilter - A filter that answers <code>true</code> if<br>" +
1342              "the node is a child of a node that is acceptable to a certain filter.<br>" +
1343              "This can be made recursive, which means the acceptable parent can be<br>" +
1344              "further up the heirarchy than just the immediate parent node.</li>" +
1345              "<li>HasChildFilter - A filter that answers <code>true</code> if<br>" +
1346              "the node is a parent of a node that is acceptable to a certain filter.<br>" +
1347              "This can be made recursive, which means the acceptable child can be<br>" +
1348              "further down the heirarchy than just the immediate children nodes.</li>" +
1349              "<li>HasSiblingFilter - A filter that answers <code>true</code> if<br>" +
1350              "the node is a sibling (they have a common parent) of a node that is<br>" +
1351              "acceptable to a certain filter.</li>" +
1352              "</ul>" +
1353              "</html>";
1354          String close = "Close";
1355          JOptionPane.showOptionDialog (
1356          // not .showMessageDialog(
1357              mMainPanel,
1358              instructions,
1359              "FilterBuilder Instructons",
1360              // remove this:
1361              JOptionPane.DEFAULT_OPTION,
1362              JOptionPane.INFORMATION_MESSAGE,
1363              // and remove rest of these:
1364              null,
1365              new String[] { close },
1366              close);
1367      }
1368  
1369      /**
1370       * The action to take when "Tutorial" menu pressed.
1371       */
1372      protected void tutorialAction ()
1373      {
1374          String instructions =
1375              "<html>" +
1376              "To get the title text from a page:<br>" +
1377              "<ul><li>Choose 'New' from the File menu.</li>" +
1378              "<li>Choose 'AndFilter' from the Filter menu.</li>" +
1379              "<li>Select the And filter so it is highlighted.</li>" +
1380              "<li>Choose 'HasParent' from the Filter menu.</li>" +
1381              "<li>Toggle the 'Recursive' checkbox on in the HasParent filter.</li>" +
1382              "<li>Select the HasParent filter so it is highlighted.</li>" +
1383              "<li>Choose 'TagName' from the Filter menu.<br>" +
1384              "<i>Alternatively, you can drag the TagName filter (icon Hello-BOB)<br>" +
1385              "from the toolbar and drop inside the HasParent filter</i></li>" +
1386              "<li>Choose 'TITLE' from the TagName combo-box.</li>" +
1387              "<li>Select the And filter and choose 'Execute Filter' from the<br>" +
1388              "Operations menu to test it.</li>" +
1389              "<li>If there is unwanted non-text nodes in the result<br>" +
1390              "select the And filter and choose 'RegexFilter' from the Filter menu.</li>" +
1391              "<li>Test it again, as above.</li>" +
1392              "<li>Choose 'Save' from the File menu and enter a filename like GetTitle.java</li>" +
1393              "<li>Compile the java file and run it.</li></ul>" +
1394              "</html>";
1395          String close = "Close";
1396          JOptionPane.showOptionDialog (
1397          // not .showMessageDialog(
1398              mMainPanel,
1399              instructions,
1400              "FilterBuilder Tutorial",
1401              // remove this:
1402              JOptionPane.DEFAULT_OPTION,
1403              JOptionPane.INFORMATION_MESSAGE,
1404              // and remove rest of these:
1405              null,
1406              new String[] { close },
1407              close);
1408      }
1409  
1410      /**
1411       * The action to take when "Hints" menu pressed.
1412       */
1413      protected void hintsAction ()
1414      {
1415          String instructions =
1416              "<html>" +
1417              "Hints:<br>" +
1418              "<ul><li>There is no undo yet, so save often.</li>" +
1419              "<li>Recursive HasParent and HasChild filters can be costly.</li>" +
1420              "<li>RegexFilter is more expensive than StringFilter.</li>" +
1421              "<li>The order of predicates in And and Or filters matters for performance,<br>" +
1422              "put cheap tests first.</li>" +
1423              "<li>The same node may show up more than once in the results,<br>" +
1424              "and at more than one nesting depth, depending on the filter used.</li>" +
1425              "<li>Typing in a tag name in the TagName filter is not recommended,<br>" +
1426              "since extraneous characters can be added. Use an item from the list instead.</li></ul>" +
1427              "</html>";
1428          String close = "Close";
1429          JOptionPane.showOptionDialog (
1430          // not .showMessageDialog(
1431              mMainPanel,
1432              instructions,
1433              "FilterBuilder Hints",
1434              // remove this:
1435              JOptionPane.DEFAULT_OPTION,
1436              JOptionPane.INFORMATION_MESSAGE,
1437              // and remove rest of these:
1438              null,
1439              new String[] { close },
1440              close);
1441      }
1442  
1443      /**
1444       * The action to take when "About" menu or button pressed.
1445       */
1446      protected void aboutAction ()
1447      {
1448          String close = "Close";
1449          JOptionPane.showOptionDialog (
1450          // not .showMessageDialog(
1451              mMainPanel,
1452              "<html><center><font color=black>The HTML Parser <font color=blue><b>FilterBuilder</b></font><br><i>by Derrick Oswald</i>&nbsp;&nbsp;<b>DerrickOswald@users.sourceforge.net</b><br>http://htmlparser.org<br><br><font size=-2>Copyright &copy; 2005</font></center></html>",
1453              "About FilterBuilder",
1454              // remove this:
1455              JOptionPane.DEFAULT_OPTION,
1456              JOptionPane.INFORMATION_MESSAGE,
1457              // and remove rest of these:
1458              null,
1459              new String[] { close },
1460              close);
1461      }
1462  
1463      /**
1464       * The action to take when "Expand" menu chosen.
1465       */
1466      public void expandAction ()
1467      {
1468          setExpanded (getSelection (), true, false);
1469      }
1470  
1471      /**
1472       * The action to take when "Collapse" menu chosen.
1473       */
1474      public void collapseAction ()
1475      {
1476          setExpanded (getSelection (), false, false);
1477      }
1478  
1479      /**
1480       * The action to take when "Expand All" menu chosen.
1481       */
1482      public void expandAllAction ()
1483      {
1484          setExpanded (getSelection (), true, true);
1485      }
1486  
1487      /**
1488       * The action to take when "Collapse" menu chosen.
1489       */
1490      public void collapseAllAction ()
1491      {
1492          setExpanded (getSelection (), false, true);
1493      }
1494  
1495      /**
1496       * Set up mouse listeners.
1497       * Sets <code>this</code> up to listen to each command
1498       * in the list as a MouseListener.
1499       * Recursively descends the tree adding to all contained elements also.
1500       * @param filters The container with commands in it.
1501       */
1502      public void setupMouseListeners (Filter[] filters)
1503      {
1504          SubFilterList list;
1505  
1506          for (int i = 0; i < filters.length; i++)
1507          {
1508              // set us up as a mouse listener on it
1509              ((Component)filters[i]).addMouseListener (this);
1510              ((Component)filters[i]).addMouseMotionListener (this);
1511              list = getEnclosed (filters[i]);
1512              if (null != list)
1513                  setupMouseListeners (list.getFilters ());
1514          }
1515      }
1516  
1517      /**
1518       * Set up drop targets.
1519       * Recursively descends the filter tree and sets up
1520       * the filter lists as drop targets.
1521       * @param filters The container with filters in it.
1522       */
1523      public void setupDropTargets (Filter[] filters)
1524      {
1525          SubFilterList list;
1526          Component[] components;
1527  
1528          for (int i = 0; i < filters.length; i++)
1529          {
1530              list = getEnclosed (filters[i]);
1531              if (null != list)
1532              {
1533                  components = list.getDropTargets ();
1534                  for (int j = 0; j < components.length; j++)
1535                      new DropTarget (components[j], this);
1536                  setupDropTargets (list.getFilters ());
1537              }
1538          }
1539      }
1540  
1541      /**
1542       * Expand or collapse filters, possibly recursively.
1543       * @param filters The list of filters to expand or collapse.
1544       * @param expanded If <code>true</code> the filters are expanded,
1545       * otherwise they are collapsed.
1546       * @param recursive If <code>true</code> the filters are processed
1547       * recursively.
1548       */
1549      public void setExpanded (
1550          Filter[] filters,
1551          boolean expanded,
1552          boolean recursive)
1553      {
1554          SubFilterList list;
1555  
1556          for (int i = 0; i < filters.length; i++)
1557          {
1558              if (recursive && (null != (list = getEnclosed (filters[i]))))
1559                  setExpanded (list.getFilters (), expanded, recursive);
1560              filters[i].setExpanded (expanded);
1561          }
1562      }
1563  
1564      /**
1565       * Retrieve the top level filters in the main window.
1566       * @return The top level filters.
1567       */
1568      public Filter[] getFilters ()
1569      {
1570          Component[] components;
1571          Filter[] ret;
1572  
1573          components = mMainPanel.getComponents ();
1574          ret = new Filter[components.length];
1575          System.arraycopy (components, 0, ret, 0, components.length);
1576          
1577          return (ret);
1578      }
1579  
1580      /**
1581       * Redo the layout.
1582       */
1583      public void relayout ()
1584      {
1585          mMainPanel.invalidate ();
1586          mMainScroller.invalidate ();
1587          mMainScroller.validate ();
1588          mMainScroller.repaint ();
1589      }
1590  
1591      /**
1592       * Read a workspace from file.
1593       * The current contents are erased.
1594       * @param name The name of the file to open.
1595       */
1596      public void open (String name)
1597      {
1598          LineNumberReader reader;
1599          String line;
1600          Filter[] filters;
1601          Point point;
1602          Dimension dimension;
1603  
1604          try
1605          {
1606              reader = new LineNumberReader (new FileReader (name));
1607              while (null != (line = reader.readLine ()))
1608                  if (line.startsWith ("// ["))
1609                  {
1610                      line = line.substring (3);
1611      	            try
1612      	            {
1613      	                filters = Filter.reconstitute (line, new Parser (mURLField.getText ()));
1614      	                mMainPanel.removeAll ();
1615      	                point = new Point ();
1616      	                for (int i = 0; i < filters.length; i++)
1617      	                {
1618      	                    dimension = filters[i].getPreferredSize ();
1619      	                    mMainPanel.add (filters[i]);
1620      	                    filters[i].setLocation (point);
1621      	                    point.y += dimension.height;
1622      	                }
1623      	                setupMouseListeners (filters);
1624      	                setupDropTargets (filters);
1625      	                relayout ();
1626      	            }
1627      	            catch (ParserException pe)
1628      	            {
1629      	                pe.printStackTrace ();
1630      	            }
1631                      break;
1632                  }
1633              reader.close ();
1634          }
1635          catch (IOException ioe)
1636          {
1637              ioe.printStackTrace ();
1638          }
1639      }
1640  
1641      /**
1642       * Exit back to the operating system.
1643       */
1644      void exitApplication ()
1645      {
1646          this.setVisible (false);    // hide the Frame
1647          this.dispose ();            // free the system resources
1648          System.exit (0);            // close the application
1649      }
1650  
1651      /**
1652       * Show a pop up context menu.
1653       * Shows a context sensitive popup menu at the location of the
1654       * mouse event.
1655       * @param event The mouse event that initiates the popup.
1656       */
1657      public void showContextMenu (MouseEvent event)
1658      {
1659          JPopupMenu menu;
1660          JMenuItem item;
1661  
1662          menu = new JPopupMenu ();
1663          menu.setName ("Popup");
1664  
1665          item = new JMenuItem ("Expand");
1666          item.setActionCommand ("expandAction");
1667          item.addActionListener (this);
1668          menu.add (item);
1669  
1670          item = new JMenuItem ("Collapse");
1671          item.setActionCommand ("collapseAction");
1672          item.addActionListener (this);
1673          menu.add (item);
1674  
1675          menu.addSeparator ();
1676  
1677          item = new JMenuItem ("Expand All");
1678          item.setActionCommand ("expandAllAction");
1679          item.addActionListener (this);
1680          menu.add (item);
1681  
1682          item = new JMenuItem ("CollapseAll");
1683          item.setActionCommand ("collapseAllAction");
1684          item.addActionListener (this);
1685          menu.add (item);
1686  
1687          menu.addSeparator ();
1688  
1689          item = new JMenuItem ("Cut");
1690          item.setActionCommand ("cutAction");
1691          item.addActionListener (this);
1692          menu.add (item);
1693  
1694          item = new JMenuItem ("Copy");
1695          item.setActionCommand ("copyAction");
1696          item.addActionListener (this);
1697          menu.add (item);
1698  
1699          item = new JMenuItem ("Paste");
1700          item.setActionCommand ("pasteAction");
1701          item.addActionListener (this);
1702          menu.add (item);
1703  
1704          item = new JMenuItem ("Delete");
1705          item.setActionCommand ("deleteAction");
1706          item.addActionListener (this);
1707          menu.add (item);
1708  
1709          menu.addSeparator ();
1710  
1711          item = new JMenuItem ("Execute Filter");
1712          item.setActionCommand ("executeAction");
1713          item.addActionListener (this);
1714          menu.add (item);
1715  
1716          menu.show (event.getComponent (), event.getX (), event.getY ());
1717      }
1718  
1719      //
1720      // selection manipulation
1721      //
1722  
1723      /**
1724       * Add a filter to the current selection set.
1725       * @param filter The filter to add.
1726       */
1727      protected void addSelection (Filter filter)
1728      {
1729          if (!selectionContains (filter))
1730              mSelection.addElement (filter);
1731          filter.setSelected (true);
1732          mMoved = false;
1733      }
1734  
1735      /**
1736       * Remove a filter from the current selection set.
1737       * @param filter The filter to remove.
1738       */
1739      protected void removeSelection (Filter filter)
1740      {
1741          mSelection.removeElement (filter); // no harm if not contained
1742          filter.setSelected (false);
1743      }
1744  
1745      /**
1746       * Select(highlight)/deselect the current selection set.
1747       * @param select If <code>true</code> turn on highlighting,
1748       * turn it off otherwise.
1749       */
1750      protected void selectSelection (boolean select)
1751      {
1752          int count;
1753          Filter filter;
1754          
1755          count = mSelection.size ();
1756          for (int i = 0; i < count; i++)
1757          {
1758              filter = (Filter)mSelection.elementAt (i);
1759              filter.setSelected (select);
1760          }
1761      }
1762  
1763      /**
1764       * Clear (empty) the current selection set.
1765       */
1766      protected void clearSelection ()
1767      {
1768          selectSelection (false);
1769          mSelection.removeAllElements ();
1770      }
1771  
1772      /**
1773       * Move the current selection set as a group.
1774       * @param translation The displacement to move them all by.
1775       */
1776      protected void moveSelection (Point translation)
1777      {
1778          int count;
1779          Filter filter;
1780          Point point;
1781          
1782          count = mSelection.size ();
1783          for (int i = 0; i < count; i++)
1784          {
1785              filter = (Filter)mSelection.elementAt (i);
1786              point = filter.getLocation ();
1787              point.translate (translation.x, translation.y);
1788              synchronized (filter.getTreeLock ())
1789              {
1790                  filter.setLocation (point.x, point.y);
1791              }
1792          }
1793          mMoved = true;
1794      }
1795  
1796      /**
1797       * Check if the current selection set contains the given filter.
1798       * @param filter The filter to check.
1799       * @return <code>true</code> if the filter is a member,
1800       * <code>false</code> otherwise.
1801       */
1802      protected boolean selectionContains (Filter filter)
1803      {
1804          return (mSelection.contains (filter));
1805      }
1806  
1807      /**
1808       * Return the last filter added to the selection set.
1809       * @return The last filter added or <code>null</code> if the current
1810       * selection set is empty.
1811       */
1812      protected Filter lastSelected ()
1813      {
1814          Filter ret;
1815          
1816          ret = null;
1817  
1818          if (0 < mSelection.size ())
1819              ret = (Filter)mSelection.lastElement ();
1820              
1821          return (ret);
1822      }
1823  
1824      /**
1825       * Return the current selection set as an array.
1826       * @return The array of selected filters.
1827       */
1828      protected Filter[] getSelection ()
1829      {
1830          Filter[] ret;
1831  
1832          ret = new Filter[mSelection.size ()];
1833          mSelection.copyInto (ret);
1834  
1835          return (ret);
1836      }
1837  
1838      /**
1839       * Serialize the current selection set.
1840       * @return The serialized form of the set of filters.
1841       */
1842      public String serializeSelection ()
1843      {
1844          Filter[] filters;
1845          StringWriter writer;
1846          PrintWriter out;
1847  
1848          filters = getSelection ();
1849          writer = new StringWriter (200);
1850          out = new PrintWriter (writer, false);
1851          try
1852          {
1853              out.println (Filter.deconstitute (filters));
1854          }
1855          catch (IOException ioe)
1856          {
1857              ioe.printStackTrace ();
1858          }
1859          finally
1860          {
1861              out.close ();
1862          }
1863          
1864          return (writer.getBuffer ().toString ());
1865      }
1866  
1867      /**
1868       * Delete the current selection set from the filters in the GUI.
1869       */
1870      public void deleteSelection ()
1871      {
1872          Filter[] filters;
1873          SubFilterList list;
1874  
1875          filters = getSelection ();
1876          for (int i = 0; i < filters.length; i++)
1877          {
1878              list = getEnclosing (filters[i]);
1879              if (null != list)
1880                  list.removeFilter (filters[i]);
1881              else
1882                  mMainPanel.remove (filters[i]);
1883          }
1884          mSelection.clear ();
1885      }
1886      
1887      /**
1888       * Check if there is more than one filter selected.
1889       * @return <code>true</code> if only one filter is selected,
1890       * <code>false</code> otherwise.
1891       */
1892      public boolean isSingleSelection ()
1893      {
1894          return (1 == mSelection.size());
1895      }
1896  
1897      //
1898      // MouseListener interface
1899      //
1900  
1901      /**
1902       * Invoked when the mouse has been clicked on a component.
1903       * @param event The mouse clicked event.
1904       */
1905      public void mouseClicked (MouseEvent event)
1906      {
1907          Object component;
1908          Filter filter;
1909          SubFilterList list;
1910          int modifiers;
1911          boolean contained;
1912          Filter[] filters;
1913          
1914          component = event.getSource ();
1915          if (component instanceof Filter)
1916          {
1917              filter = (Filter)component;
1918              modifiers = event.getModifiers ();
1919              contained = selectionContains (filter);
1920              
1921              if (0 != (modifiers & InputEvent.SHIFT_MASK))
1922              {
1923                  // add everything from last selected to this command
1924                  list = getEnclosed (filter);
1925                  if (null != list)
1926                      filters = list.getFilters ();
1927                  else
1928                      filters = getFilters ();
1929                  Filter last = lastSelected ();
1930                  if (null == last)
1931                      addSelection (filter);
1932                  else
1933                  {
1934                      int current = -1;
1935                      int recent = -1;
1936                      for (int i = 0; i < filters.length; i++)
1937                      {
1938                          if (filters[i] == filter)
1939                              current = i;
1940                          if (filters[i] == last)
1941                              recent = i;
1942                      }
1943                      if ((current != -1) && (recent != -1))
1944                          for (int i = Math.min (current, recent);
1945                              i <= Math.max (current, recent); i++)
1946                          addSelection (filters[i]);
1947                  }
1948              }
1949              else if (0 != (modifiers & InputEvent.CTRL_MASK))
1950              {
1951                  // add just the new command
1952                  if (contained)
1953                      removeSelection (filter);
1954                  else
1955                      addSelection (filter);
1956              }
1957              else if (0 != (modifiers & InputEvent.BUTTON3_MASK))
1958              {
1959                  if (!selectionContains (filter))
1960                  {
1961  	                clearSelection ();
1962  	                addSelection (filter);
1963                  }
1964                  showContextMenu (event);
1965              }
1966              else
1967              {
1968                  clearSelection ();
1969                  addSelection (filter);
1970              }
1971          }
1972          else
1973              clearSelection ();
1974      }
1975  
1976      /**
1977       * Invoked when a mouse button has been released on a component.
1978       * @param event The mouse released event.
1979       */
1980      public void mouseReleased (MouseEvent event)
1981      {
1982      }
1983  
1984      /**
1985       * Invoked when the mouse enters a component.
1986       * @param event The mouse entered event.
1987       */
1988      public void mouseEntered (MouseEvent event)
1989      {
1990      }
1991  
1992      /**
1993       * Invoked when the mouse exits a component.
1994       * @param event The mouse exited event.
1995       */
1996      public void mouseExited (MouseEvent event)
1997      {
1998      }
1999  
2000      /**
2001       * Invoked when a mouse button has been pressed on a component.
2002       * @param event The mouse pressed event.
2003       */
2004      public void mousePressed (MouseEvent event)
2005      {
2006          Object component;
2007          Point newpoint;
2008          Point upperleft;
2009          
2010          component = event.getSource ();
2011          if (component instanceof Filter)
2012          {
2013              // translate the point relative to the enclosing container
2014              newpoint = event.getPoint ();
2015              upperleft = ((Component)component).getLocation ();
2016              newpoint.translate (upperleft.x, upperleft.y);
2017              setBasePoint (newpoint);
2018          }
2019          else
2020              setBasePoint (null);
2021      }
2022  
2023      //
2024      // MouseMotionListener interface
2025      //
2026      
2027      /**
2028       * Mouse drag notification.
2029       * Invoked when a mouse button is pressed on a component and
2030       * then dragged. Mouse drag events will continue to be
2031       * delivered to the component where the first originated
2032       * until the mouse button is released (regardless of whether
2033       * the mouse position is within the bounds of the component).
2034       * @param event The mouse drag event.
2035       */
2036      public synchronized void mouseDragged (MouseEvent event)
2037      {
2038          Object component;
2039          Filter filter;
2040          Point base;
2041          Point newpoint;
2042          Point upperleft;
2043          Point translation;
2044          
2045          component = event.getSource ();
2046          if (component instanceof Filter)
2047          {
2048              filter = (Filter)component;
2049              if (selectionContains (filter)) // drag on a selected item
2050              {
2051                  if (null == getEnclosing (filter)) // not contained
2052                      try
2053                      {
2054                          base = getBasePoint ();
2055                          if (null != base)
2056                          {
2057                              newpoint = event.getPoint ();
2058                              // translate the point relative to the enclosing container
2059                              upperleft = filter.getLocation ();
2060                              newpoint.translate (upperleft.x, upperleft.y);
2061                              // get the difference between this point and the old base
2062                              translation = new Point (
2063                                  newpoint.x - base.x,
2064                                  newpoint.y - base.y);
2065                              // update the base point
2066                              setBasePoint (newpoint);
2067                              // apply this difference to the selection
2068                              moveSelection (translation);
2069                          }    
2070                      }
2071                      catch (Exception e)
2072                      {
2073                      }
2074              }
2075              else
2076                  mouseClicked (event); // a small slip shouldn't stop a click
2077          }
2078      }
2079  
2080      /**
2081       * Mouse move notification.
2082       * Invoked when the mouse button has been moved on a component
2083       * (with no buttons no down).    
2084       * @param event The mouse moved event.
2085       */
2086      public void mouseMoved (MouseEvent event)
2087      {
2088      }
2089  
2090      //
2091      // WindowListener interface
2092      //
2093  
2094      /**
2095       * Invoked the first time a window is made visible.
2096       * <i>Not used.</i>
2097       * @param event The window event.
2098       */
2099      public void windowOpened (WindowEvent event) {}
2100  
2101      /**
2102       * Handles window closing event.
2103       * Performs function <code>exitApplication()</code>.
2104       * @param event The window event.
2105       */
2106      public void windowClosing (WindowEvent event)
2107      {
2108          if (event.getSource () == this)
2109              exitApplication ();
2110      }
2111  
2112      /**
2113       * Invoked when a window has been closed as the result
2114       * of calling dispose on the window.
2115       * <i>Not used.</i>
2116       * @param event The window event.
2117       */
2118      public void windowClosed (WindowEvent event) {}
2119  
2120      /**
2121       * Invoked when a window is changed from a normal to a
2122       * minimized state. For many platforms, a minimized window 
2123       * is displayed as the icon specified in the window's 
2124       * iconImage property.
2125       * <i>Not used.</i>
2126       * @param event The window event.
2127       */
2128      public void windowIconified (WindowEvent event) {}
2129  
2130      /**
2131       * Invoked when a window is changed from a minimized
2132       * to a normal state.
2133       * <i>Not used.</i>
2134       * @param event The window event.
2135       */
2136      public void windowDeiconified (WindowEvent event) {}
2137  
2138      /**
2139       * Invoked when the window is set to be the user's
2140       * active window, which means the window (or one of its
2141       * subcomponents) will receive keyboard events.
2142       * <i>Not used.</i>
2143       * @param event The window event.
2144       */
2145      public void windowActivated (WindowEvent event) {}
2146  
2147      /**
2148       * Invoked when a window is no longer the user's active
2149       * window, which means that keyboard events will no longer
2150       * be delivered to the window or its subcomponents.
2151       * <i>Not used.</i>
2152       * @param event The window event.
2153       */
2154      public void windowDeactivated (WindowEvent event) {}
2155  
2156      //
2157      // ActionListener interface
2158      //
2159  
2160      /**
2161       * Handles menu and toolbar item choices.
2162       * @param event The action even that triggers this function.
2163       */
2164      public void actionPerformed (ActionEvent event)
2165      {
2166          Object object;
2167          String action;
2168          
2169          object = event.getSource();
2170  //        if (object instanceof JButton)
2171  //        {
2172  //            String url;
2173  //            url = mURLField.getText ();
2174  //            mURLField.selectAll ();
2175  //            //setURL (url);
2176  //        }
2177          if (object instanceof JButton)
2178              action = ((JButton)object).getActionCommand ();
2179          else if (object instanceof JMenuItem)
2180              action = ((JMenuItem)object).getActionCommand ();
2181          else
2182              action = null;
2183  
2184          if (object instanceof Component)
2185              mCurrentComponent = (Component)object;
2186  
2187          if (null != action)
2188              try
2189              {
2190                  Method method = this.getClass ().getDeclaredMethod (action, new Class[0]);
2191                  method.invoke (this, new Object[0]);
2192              }
2193              catch (NoSuchMethodException nsme)
2194              {
2195                  System.out.println ("no " + action + " method found");
2196              }
2197              catch (Exception e)
2198              {
2199                  e.printStackTrace ();
2200              }
2201      }
2202  
2203      //
2204      // ClipboardOwner interface
2205      //
2206  
2207      /**
2208       * Notifies this object that it is no longer the owner
2209       * of the contents of the clipboard.
2210       * @param clipboard The clipboard that is no longer owned.
2211       * @param contents The contents which this owner had placed on the clipboard.
2212       */
2213      public void lostOwnership (Clipboard clipboard, Transferable contents)
2214      {
2215          System.out.println ("lost clipboard ownership");
2216      }
2217  
2218      //
2219      // DragGestureListener interface
2220      //
2221  
2222      /**
2223       * A DragGestureRecognizer has detected a platform-dependent drag initiating gesture.
2224       * It is notifying this listener in order for it to initiate the action for the user.
2225       * @param event The DragGestureEvent describing the gesture that has just occurred.
2226       */
2227      public void dragGestureRecognized (DragGestureEvent event)
2228      {
2229          Component component;
2230          String cls;
2231          Filter filter;
2232          StringSelection text;
2233          
2234          
2235          component = event.getComponent ();
2236          try
2237          {
2238              cls = component.getName ();  // (String)Filter.mWrappers.get (component.getName ());
2239              if (null != cls)
2240              {
2241                  filter = Filter.instantiate (cls);
2242                  text = new StringSelection (Filter.deconstitute (new Filter[] { filter })); 
2243                  mDragSource.startDrag (event, DragSource.DefaultMoveDrop, text, this);
2244              }
2245          }
2246          catch (Exception e)
2247          {
2248              e.printStackTrace ();
2249          }
2250      }
2251  
2252      //
2253      // DragSourceListener interface
2254      //
2255  
2256      /**
2257       * This message goes to DragSourceListener,
2258       * informing it that the dragging has ended.
2259       * @param event Details about the drop event.
2260       */
2261      public void dragDropEnd (DragSourceDropEvent event)
2262      {   
2263          if (event.getDropSuccess ())
2264          {
2265              // System.out.println ("added new class");
2266          }
2267      }
2268  
2269      /**
2270       * This message goes to DragSourceListener,
2271       * informing it that the dragging has entered the DropSite.
2272       * @param event Details about the drag event.
2273       */
2274      public void dragEnter (DragSourceDragEvent event)
2275      {
2276          // System.out.println ("dragEnter");
2277      }
2278  
2279      /**
2280       * This message goes to DragSourceListener, informing it that the dragging 
2281       * has exited the DropSite.
2282       * @param event Details about the drag event.
2283       */
2284      public void dragExit (DragSourceEvent event)
2285      {
2286          // System.out.println( "dragExit");
2287      }
2288  
2289      /**
2290       * This message goes to DragSourceListener, informing it that the dragging is currently 
2291       * ocurring over the DropSite.
2292       * @param event Details about the drag event.
2293       */
2294      public void dragOver (DragSourceDragEvent event)
2295      {
2296          // System.out.println( "dragExit");
2297      }
2298  
2299      /**
2300       * This is invoked when the user changes the dropAction.
2301       * @param event Details about the drop action event.
2302       */
2303      public void dropActionChanged (DragSourceDragEvent event)
2304      {
2305          // System.out.println( "dropActionChanged"); 
2306      }
2307  
2308      //
2309      // DropTargetListener interface
2310      //
2311  
2312      /**
2313       * This is invoked when you are dragging over the DropSite.
2314       * @param event Details about the drag event.
2315       */
2316      public void dragEnter (DropTargetDragEvent event)
2317      {
2318          // debug messages for diagnostics 
2319          // event.acceptDrag (DnDConstants.ACTION_MOVE);
2320          // System.out.println ("dragEnter");
2321          DropTargetContext context;
2322          Component component;
2323          SubFilterList list;
2324  
2325          // find the enclosing filter
2326          context = event.getDropTargetContext ();
2327          component = context.getComponent ();
2328          while (     (null != component)
2329                  && !(component instanceof SubFilterList)
2330                  && !(component == mMainPanel))
2331              component = component.getParent ();
2332          if (component instanceof SubFilterList)
2333              list = (SubFilterList)component;
2334          else
2335              list = null;
2336          // so either list is the enclosing list,
2337          // or list is null and the target component is the main panel
2338  
2339          if (null != list)
2340              if (!list.canAccept ())
2341                  event.rejectDrag ();
2342              else
2343                  list.setSelected (true);
2344      }
2345  
2346      /**
2347       * Thi ss invoked when you are exit the DropSite without dropping.
2348       * @param event Details about the drag event.
2349       */
2350      public void dragExit (DropTargetEvent event)
2351      {
2352          // debug messages for diagnostics 
2353          // event.acceptDrag (DnDConstants.ACTION_MOVE);
2354          // System.out.println ("dragEnter");
2355          DropTargetContext context;
2356          Component component;
2357          SubFilterList list;
2358  
2359          // find the enclosing filter
2360          context = event.getDropTargetContext ();
2361          component = context.getComponent ();
2362          while (     (null != component)
2363                  && !(component instanceof SubFilterList)
2364                  && !(component == mMainPanel))
2365              component = component.getParent ();
2366          if (component instanceof SubFilterList)
2367              list = (SubFilterList)component;
2368          else
2369              list = null;
2370          // so either list is the enclosing list,
2371          // or list is null and the target component is the main panel
2372  
2373          if (null != list)
2374              list.setSelected (false);
2375      }
2376  
2377      /**
2378       * This is invoked when a drag operation is going on.
2379       * @param event Details about the drag event.
2380       */
2381      public void dragOver (DropTargetDragEvent event)
2382      {
2383          // System.out.println( "dragOver");
2384      }
2385  
2386      /**
2387       * This is invoked when a drop has occurred.
2388       * @param event The drop event.
2389       */
2390      public void drop (DropTargetDropEvent event)
2391      {
2392          DropTargetContext context;
2393          Component component;
2394          SubFilterList list;
2395          String s;
2396          Point point;
2397          Filter[] filters;
2398          boolean accept;
2399  
2400          // find the enclosing filter
2401          context = event.getDropTargetContext ();
2402          component = context.getComponent ();
2403          while (     (null != component)
2404                  && !(component instanceof SubFilterList)
2405                  && !(component == mMainPanel))
2406              component = component.getParent ();
2407          if (component instanceof SubFilterList)
2408              list = (SubFilterList)component;
2409          else
2410              list = null;
2411          // so either list is the enclosing list,
2412          // or list is null and the target component is the main panel
2413  
2414          try 
2415          {
2416              accept = false;
2417              Transferable transferable = event.getTransferable();
2418                                     
2419              // we accept only Strings      
2420              if (transferable.isDataFlavorSupported (DataFlavor.stringFlavor))
2421              {
2422                  accept = true;
2423                  event.acceptDrop (DnDConstants.ACTION_MOVE);
2424                  s = (String)transferable.getTransferData (DataFlavor.stringFlavor);
2425                  point = event.getLocation ();
2426                  try
2427                  {
2428                      // get the filter and add into the target
2429                      filters = Filter.reconstitute (s, new Parser (mURLField.getText ()));
2430                      if (0 < filters.length)
2431                          insertFilters (filters, point, list);
2432                      if (null != list)
2433                          list.setSelected (false);
2434                  }
2435                  catch (Exception e)
2436                  {
2437                      e.printStackTrace ();
2438                  }
2439                  // signal the drop was successful
2440                  context.dropComplete (accept);
2441              } 
2442              else
2443                  event.rejectDrop();
2444          }
2445          catch (IOException exception)
2446          {
2447              exception.printStackTrace();
2448              System.err.println( "Exception" + exception.getMessage());
2449              event.rejectDrop();
2450          } 
2451          catch (UnsupportedFlavorException ufException)
2452          {
2453              ufException.printStackTrace();
2454              System.err.println( "Exception" + ufException.getMessage());
2455              event.rejectDrop();
2456          }
2457      }
2458  
2459      /**
2460       * This is invoked if the user modifies the current drop gesture.
2461       * @param event Details about the drop action change event.
2462       */
2463      public void dropActionChanged (DropTargetDragEvent event)
2464      {
2465          // System.out.println( "dropActionChanged");
2466      }
2467  
2468      /**
2469       * The entry point for this application.
2470       * Creates a new FilterBuilder and makes it visible.
2471       * @param args [0] optional URL to operate on.
2472       */
2473      public static void main (String args[])
2474      {
2475          try
2476          {
2477              // set the Look and Feel to the the native system
2478  //            try
2479  //            {
2480  //                javax.swing.UIManager.setLookAndFeel (javax.swing.UIManager.getSystemLookAndFeelClassName ());
2481  //            } 
2482  //            catch (Exception e)
2483  //            { 
2484  //            }
2485  
2486              // create a new instance of our application's frame, and make it visible
2487              FilterBuilder builder = new FilterBuilder ();
2488              if (0 != args.length)
2489                  builder.mURLField.setText (args[0]);
2490              builder.setVisible (true);
2491          } 
2492          catch (Throwable t)
2493          {
2494              t.printStackTrace ();
2495              // ensure the application exits with an error condition
2496              System.exit (1);
2497          }
2498      }
2499  
2500  }