// HTMLParser Library $Name: v1_6_20060319 $ - A java-based parser for HTML
// http://sourceforge.org/projects/htmlparser
// Copyright (C) 2005 Derrick Oswald
//
// Revision Control Information
//
// $Source: /cvsroot/htmlparser/htmlparser/src/org/htmlparser/parserapplications/filterbuilder/FilterBuilder.java,v $
// $Author: derrickoswald $
// $Date: 2005/04/12 11:27:42 $
// $Revision: 1.5 $
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package org.htmlparser.parserapplications.filterbuilder;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.FileDialog;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Vector;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
//import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
import org.htmlparser.Parser;
import org.htmlparser.beans.FilterBean;
import org.htmlparser.parserapplications.filterbuilder.layouts.NullLayoutManager;
import org.htmlparser.util.EncodingChangeException;
import org.htmlparser.util.NodeIterator;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
/**
* The main program for the FilterBuilder programming system.
* <p>ToDo:
* <ul>
* <li>thread the attribute fetching</li>
* <li>CSS selector filter</li>
* <li>table row filter</li>
* <li>table column filter</li>
* <li>trigger filter</li>
* <li>undo</li>
* <li>handle bad URLs</li>
* <li>StringBean type secondary text output</li>
* <li>context sensitive menus</li>
* </ul>
*/
public class FilterBuilder
extends
JFrame
implements
WindowListener,
ActionListener,
MouseListener,
MouseMotionListener,
DragGestureListener,
DragSourceListener,
DropTargetListener,
ClipboardOwner
{
static final String TITLE = "HTML Parser FilterBuilder";
static final URL mDocumentBase;
static
{
String p;
char ps;
URL base;
p = System.getProperty ("user.dir");
// if the system file separator isn't the URL file separator convert it.
try
{
ps = (System.getProperty ("file.separator")).charAt(0);
if ('/' != ps)
p.replace (ps, '/');
}
catch (StringIndexOutOfBoundsException e)
{
}
try
{
base = new URL ("file:///" + p + "/");
}
catch (MalformedURLException murle)
{
base = null;
}
mDocumentBase = base;
}
static String mHomeDir;
static
{
String dir;
File file;
dir = System.getProperty ("user.home")
+ System.getProperty ("file.separator")
+ ".htmlparser";
file = new File (dir);
if (!file.exists ())
if (!file.mkdirs ()) // make the directory if it doesn't exist
throw new RuntimeException (
"cannot create directory "
+ file.getAbsolutePath ());
mHomeDir = file.getAbsolutePath ();
}
/**
* The relative position of the mouse while dragging.
*/
protected Point mBasePoint;
/**
* Selected commands.
*/
protected Vector mSelection;
/**
* If true selection moved.
*/
protected boolean mMoved;
/**
* This component is a drop target.
*/
protected DropTarget mDropTarget;
/**
* Enables this component to be a Drag Source.
*/
protected DragSource mDragSource;
/**
* Kludge: Used by actionPerformed/filterAction to remember the filter menu item.
*/
protected Component mCurrentComponent;
/**
* The main panel GUI component.
*/
protected JPanel mMainPanel;
/**
* The main panel scrolling GUI component.
*/
protected JScrollPane mMainScroller;
/**
* The URL input GUI component.
*/
protected JTextField mURLField;
/**
* The output panel GUI component.
*/
protected JDesktopPane mOutput;
/**
* Create an FilterBuilder programming environment.
*/
public FilterBuilder ()
{
JMenuBar menubar;
JToolBar toolbar;
JMenu menu;
JPanel panel;
JScrollPane pane;
JSplitPane split;
JMenuItem item;
// drag and drop support
mMainPanel = new JPanel ();
mDropTarget = new DropTarget (mMainPanel, this);
mDragSource = new DragSource ();
// menu and toolbar
menubar = new JMenuBar();
toolbar = new JToolBar ();
toolbar.setAlignmentY (0.222222F);
// file menu
menu = new JMenu ();
menu.setText ("File");
menu.setActionCommand ("File");
menu.setMnemonic ((int)'F');
makeMenuButton ("New", "Create a new document", "New", 'N', KeyStroke.getKeyStroke (KeyEvent.VK_N, Event.CTRL_MASK), toolbar, menu);
makeMenuButton ("Open", "Open an existing document", "Open...", 'O', KeyStroke.getKeyStroke (KeyEvent.VK_O, Event.CTRL_MASK), toolbar, menu);
makeMenuButton ("Save", "Save the active document", "Save...", 'S', KeyStroke.getKeyStroke (KeyEvent.VK_S, Event.CTRL_MASK), toolbar, menu);
makeMenuButton ("SaveAs", "Save the active document", "Save As...", 'A', KeyStroke.getKeyStroke (KeyEvent.VK_A, Event.CTRL_MASK), null, menu);
menu.add (new JSeparator ());
makeMenuButton ("Exit", "Exit the program", "Exit", 'E', KeyStroke.getKeyStroke (KeyEvent.VK_E, Event.CTRL_MASK), null, menu);
menubar.add (menu);
toolbar.add(new JToolBar.Separator());
// edit menu
menu = new JMenu ();
menu.setText ("Edit");
menu.setActionCommand ("Edit");
menu.setMnemonic ((int)'E');
makeMenuButton ("Cut", "Cut the selection and put it on the Clipboard", "Cut", 'T', KeyStroke.getKeyStroke (KeyEvent.VK_X, Event.CTRL_MASK), toolbar, menu);
makeMenuButton ("Copy", "Copy the selection and put it on the Clipboard", "Copy", 'C', KeyStroke.getKeyStroke (KeyEvent.VK_C, Event.CTRL_MASK), toolbar, menu);
makeMenuButton ("Paste", "Insert Clipboard contents", "Paste", 'P', KeyStroke.getKeyStroke (KeyEvent.VK_V, Event.CTRL_MASK), toolbar, menu);
makeMenuButton ("Delete", "Delete the selection", "Delete", 'D', KeyStroke.getKeyStroke (KeyEvent.VK_DELETE, 0), toolbar, menu);
menubar.add (menu);
// filter menu
menu = new JMenu ();
menu.setText ("Filter");
menu.setActionCommand ("Filter");
menu.setMnemonic ((int)'F');
menubar.add (menu);
toolbar.add (new JToolBar.Separator());
// filters menu and filters toolbar
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.AndFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.OrFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.NotFilterWrapper");
menu.addSeparator ();
toolbar.add (new JToolBar.Separator ());
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.StringFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.RegexFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.TagNameFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.NodeClassFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasAttributeFilterWrapper");
menu.addSeparator ();
toolbar.add (new JToolBar.Separator ());
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasParentFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasChildFilterWrapper");
addFilter (menu, toolbar, "org.htmlparser.parserapplications.filterbuilder.wrappers.HasSiblingFilterWrapper");
menu.addSeparator ();
toolbar.add (new JToolBar.Separator ());
// operation menu
menu = new JMenu ();
menu.setText ("Operation");
menu.setActionCommand ("Operation");
menu.setMnemonic ((int)'r');
item = new JMenuItem ();
item.setText ("Expand");
item.setActionCommand ("expandAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ();
item.setText ("Collapse");
item.setActionCommand ("collapseAction");
item.addActionListener (this);
menu.add (item);
menu.addSeparator ();
item = new JMenuItem ();
item.setText ("Expand All");
item.setActionCommand ("expandAllAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ();
item.setText ("Collapse All");
item.setActionCommand ("collapseAllAction");
item.addActionListener (this);
menu.add (item);
menu.addSeparator ();
item = new JMenuItem ("Fetch Page");
item.setActionCommand ("fetchAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Execute Filter");
item.setActionCommand ("executeAction");
item.addActionListener (this);
menu.add (item);
menubar.add (menu);
// help menu
menu = new JMenu ();
menu.setText ("Help");
menu.setActionCommand ("Help");
menu.setMnemonic ((int)'H');
item = new JMenuItem ("Filtering");
item.setActionCommand ("filteringAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Instructions");
item.setActionCommand ("instructionsAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Tutorial");
item.setActionCommand ("tutorialAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Hints");
item.setActionCommand ("hintsAction");
item.addActionListener (this);
menu.add (item);
makeMenuButton ("About", "Display program information, version number and copyright", "About", 'B', KeyStroke.getKeyStroke (KeyEvent.VK_H, Event.CTRL_MASK), toolbar, menu);
menubar.add (menu);
setJMenuBar (menubar);
// toolbar panel
panel = new JPanel ();
panel.setLayout (new FlowLayout (FlowLayout.LEFT,0,0));
panel.add (toolbar);
getContentPane().setLayout (new BorderLayout (0,0));
getContentPane ().add (BorderLayout.NORTH, panel);
// URL entry
mURLField = new JTextField ();
mURLField.setToolTipText ("Enter the URL to view");
// mTextField.addActionListener (this);
mURLField.setText ("http://sourceforge.org/projects/htmlparser");
getContentPane().add (BorderLayout.SOUTH, mURLField);
// application setup
setTitle (TITLE);
setDefaultCloseOperation (WindowConstants.DO_NOTHING_ON_CLOSE);
setSize (640, 480);
setVisible (false);
// main panel
mMainPanel.setLayout (new NullLayoutManager ());
mMainScroller = new JScrollPane (
mMainPanel,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
split = new JSplitPane ();
pane = new JScrollPane ();
pane.setViewportView (mMainScroller);
split.setLeftComponent (pane);
mOutput = new JDesktopPane ();
split.setRightComponent (mOutput);
getContentPane().add (BorderLayout.CENTER, split);
// shenanigans to get the splitter bar at the midpoint
setVisible (true);
split.setDividerLocation (0.5);
setVisible (false);
// listeners
addWindowListener (this);
setIconImage (Toolkit.getDefaultToolkit ().getImage ("images/program16.gif"));
addMouseListener (this);
addMouseMotionListener (this);
// clipboard buffer
mSelection = new Vector ();
}
/**
* Creates a new instance of an FilterBuilder environment with the given title.
* @param title the title for the new frame.
* @see #FilterBuilder()
*/
public FilterBuilder (String title)
{
this ();
setTitle (title);
}
/**
* Makes menu and toolbar items for commands.
* @param name The name of the command.
* @param description A description for the tooltip.
* @param text The text for the menu.
* @param mnemonic The navigation mnemonic.
* @param key Accelerator key.
* @param toolbar The toolbar to add the button to.
* @param menu The menu to add the menu item to.
*/
protected void makeMenuButton (
String name,
String description,
String text,
int mnemonic,
KeyStroke key,
JToolBar toolbar,
JMenu menu)
{
JButton button;
JMenuItem item;
ImageIcon icon;
String command;
command = name.toLowerCase ();
try
{
icon = new ImageIcon (getURL ("images/" + command + ".gif"));
}
catch (java.net.MalformedURLException error)
{
icon = null;
}
item = new JMenuItem ();
item.setText (text);
item.setActionCommand (command + "Action");
item.setAccelerator (key);
item.setMnemonic (mnemonic);
item.setIcon (icon);
item.addActionListener (this);
menu.add (item);
if (null != toolbar)
{
button = new JButton ();
button.setDefaultCapable (false);
button.setToolTipText (description);
button.setMnemonic (mnemonic);
button.setActionCommand (command + "Action");
button.setMargin (new Insets (0, 0, 0, 0));
button.setIcon (icon);
button.addActionListener (this);
toolbar.add (button);
}
}
/**
* Get a url for the given resource specification.
* @param spec The name of the resource.
* @return The fully formed URL.
* @exception MalformedURLException In the case that the document base
* or name of the resource cannot be turned into a URL.
*/
protected URL getURL (String spec)
throws MalformedURLException
{
URL ret;
if (null == (ret = getClass ().getResource (spec)))
if ((null != mDocumentBase) && (-1 == spec.indexOf ("//")))
ret = new URL (mDocumentBase, spec);
else
ret = new URL (spec);
return ret;
}
/**
* Creates a new button for the given class.
* @param class_name The name of the Filter class.
* @return A fully functional button with name, tool tip,
* icon and drag recognizer.
*/
public JButton makeFilterButton (String class_name)
{
Filter filter;
JButton ret;
ret = new JButton ();
filter = Filter.instantiate (class_name);
if (null != filter)
{
ret.setName (class_name); // filter.getNodeFilter ().getClass ().getName ());
ret.setToolTipText (filter.getDescription ());
ret.setMargin (new Insets (0, 0, 0, 0));
ret.setIcon (filter.getIcon ());
mDragSource.createDefaultDragGestureRecognizer (
ret,
DnDConstants.ACTION_MOVE,
this);
ret.setActionCommand ("filterAction");
ret.addActionListener (this);
}
return (ret);
}
/**
* Add a filter to the GUI.
* Adds the filter specified by class_name to the menu, toolbar and
* starts listening for actions.
* @param menu The menu to add the filter to.
* @param toolbar The toolbar to add the filter to.
* @param class_name The class name for the filter wrapper.
* From the wrapper, the NodeFilter, description and icon can be obtained.
*/
public void addFilter (JMenu menu, JToolBar toolbar, String class_name)
{
Filter filter;
filter = Filter.instantiate (class_name);
if (null != filter)
{
String name;
String description;
Icon icon;
String text;
JMenuItem item;
name = filter.getNodeFilter ().getClass ().getName ();
description = filter.getDescription ();
icon = filter.getIcon ();
text = name.substring (name.lastIndexOf ('.') + 1);
item = new JMenuItem ();
item.setName (class_name);
item.setText (text);
item.setActionCommand ("filterAction");
// item.setAccelerator (key);
// item.setMnemonic (mnemonic);
item.setToolTipText (description);
item.setIcon (icon);
item.addActionListener (this);
menu.add (item);
toolbar.add (makeFilterButton (class_name));
}
}
/**
* Adds a set of filters to the main panel or a sublist.
* Sets up the GUI components as drop targets and mouse listeners,
* and performs a relayout to display them.
* @param filters The filter wrappers to add.
* @param point The point at which to start adding (list == null).
* @param list The list to add to (point not used), or <code>null</code>
* for the main panel.
*/
protected void insertFilters (Filter[] filters, Point point, SubFilterList list)
{
Dimension dimension;
if (null == list)
{
for (int i = 0; i < filters.length; i++)
{
filters[i].setLocation (point);
mMainPanel.add (filters[i]);
dimension = filters[i].getPreferredSize ();
point.y += dimension.height;
}
}
else
for (int i = 0; i < filters.length; i++)
list.addFilter (filters[i]);
setupDropTargets (filters);
setupMouseListeners (filters);
relayout ();
}
/**
* Sets the position of the mouse in the component.
*
* @param point The point where the mouse position is.
*/
protected void setBasePoint (Point point)
{
mBasePoint = point;
}
/**
* Gets the current base point of the mouse pointer.
* This value is used to offset the drag position
* to maintain the mouse position at the same
* relative position within the card while dragging.
*
* @return The current base point of the mouse pointer.
*/
protected Point getBasePoint ()
{
return (mBasePoint);
}
/**
* Get the enclosing sub filter list if any.
* @param component The component that's supposedly enclosed.
* @return The enclosing component or <code>null</code> otherwise.
*/
protected SubFilterList getEnclosing (Component component)
{
do
component = component.getParent ();
while ( (null != component)
&& !(component instanceof SubFilterList));
return ((SubFilterList)component);
}
/**
* Get the enclosed sub filter list if any.
* @param component The component that's supposedly enclosing the list.
* @return The enclosed component or <code>null</code> otherwise.
*/
protected SubFilterList getEnclosed (Component component)
{
Component[] list;
if (component instanceof Container)
{
list = ((Container)component).getComponents ();
for (int i = 0; i < list.length; i++)
if (list[i] instanceof SubFilterList)
return ((SubFilterList)list[i]);
}
return (null);
}
/**
* Makes a program like:
* <pre>
* // Generated by FilterBuilder. http://htmlparser.org
* // [aced0005737200206f72672e68746d6c7061727365722e66696c746572732e416e6446696c74657224c30516b2b7b2120200015b000b6d5072656469636174657374001c5b4c6f72672f68746d6c7061727365722f4e6f646546696c7465723b78707572001c5b4c6f72672e68746d6c7061727365722e4e6f646546696c7465723b8f17479b1d5f7992020000787000000002737200246f72672e68746d6c7061727365722e66696c746572732e5461674e616d6546696c746572b28b2601a614890f0200014c00056d4e616d657400124c6a6176612f6c616e672f537472696e673b78707400044d455441737200296f72672e68746d6c7061727365722e66696c746572732e48617341747472696275746546696c74657296abdfb3b0714cda0200024c000a6d41747472696275746571007e00064c00066d56616c756571007e000678707400046e616d6570]
*
* import org.htmlparser.*;
* import org.htmlparser.filters.*;
* import org.htmlparser.beans.*;
* import org.htmlparser.util.*;
*
* public class Test
* {
* public static void main (String args[])
* {
* TagNameFilter filter0 = new TagNameFilter ();
* filter0.setName ("META");
* HasAttributeFilter filter1 = new HasAttributeFilter ();
* filter1.setAttributeName ("name");
* NodeFilter[] array0 = new NodeFilter[2];
* array0[0] = filter0;
* array0[1] = filter1;
* AndFilter filter2 = new AndFilter ();
* filter2.setPredicates (array0);
* NodeFilter[] array1 = new NodeFilter[1];
* array1[0] = filter2;
* FilterBean bean = new FilterBean ();
* bean.setFilters (array1);
* if (0 != args.length)
* {
* bean.setURL (args[0]);
* System.out.println (bean.getNodes ().toHtml ());
* }
* else
* System.out.println ("Usage: java -classpath .:htmlparser.jar Test <url>");
* }
* }
* </pre>
* @param name The name of the class.
* @param out The buffer to append to.
* @param bean The bean to extract the filters from to make the program.
*/
protected void makeProgram (String name, StringBuffer out, FilterBean bean)
{
// so we need to keep track of filters and arrays of filters to give them unique numbers
// each Filter is responsible for outputting it's code and returning it's variable name
int[] context; // 0 - indent, 1 - next filter variable #, 2 - next array of filters variable #
String[] names;
Filter[] filters;
String array;
filters = (Filter[])bean.getFilters ();
context = new int[3];
context[0] = 0;
Filter.spaces (out, context[0]);
out.append ("// Generated by FilterBuilder. http://htmlparser.org");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("// ");
try
{
out.append (Filter.deconstitute (filters));
}
catch (IOException ioe)
{
ioe.printStackTrace ();
}
Filter.newline (out);
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("import org.htmlparser.*;");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("import org.htmlparser.filters.*;");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("import org.htmlparser.beans.*;");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("import org.htmlparser.util.*;");
Filter.newline (out);
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("public class ");
out.append (name);
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("{");
context[0] = 4;
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("public static void main (String args[])");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("{");
Filter.newline (out);
context[0] = 8;
names = new String [filters.length];
for (int i = 0; i < names.length; i++)
names[i] = filters[i].toJavaCode (out, context);
array = "array" + context[2]++;
Filter.spaces (out, context[0]);
out.append ("NodeFilter[] ");
out.append (array);
out.append (" = new NodeFilter[");
out.append (filters.length);
out.append ("];");
Filter.newline (out);
for (int i = 0; i < filters.length; i++)
{
Filter.spaces (out, context[0]);
out.append (array);
out.append ("[");
out.append (i);
out.append ("] = ");
out.append (names[i]);
out.append (";");
Filter.newline (out);
}
Filter.spaces (out, context[0]);
out.append ("FilterBean bean = new FilterBean ();");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("bean.setFilters (");
out.append (array);
out.append (");");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("if (0 != args.length)");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("{");
Filter.newline (out);
context[0] = 12;
Filter.spaces (out, context[0]);
out.append ("bean.setURL (args[0]);");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("System.out.println (bean.getNodes ().toHtml ());");
Filter.newline (out);
context[0] = 8;
Filter.spaces (out, context[0]);
out.append ("}");
Filter.newline (out);
Filter.spaces (out, context[0]);
out.append ("else");
Filter.newline (out);
context[0] = 12;
Filter.spaces (out, context[0]);
out.append ("System.out.println (\"Usage: java -classpath .:htmlparser.jar ");
out.append (name);
out.append (" <url>\");");
Filter.newline (out);
context[0] = 4;
Filter.spaces (out, context[0]);
out.append ("}");
Filter.newline (out);
context[0] = 0;
Filter.spaces (out, context[0]);
out.append ("}");
Filter.newline (out);
}
/**
* Extracts a java class name from a file name.
* ToDo: make this package-smart somehow.
* @param file The name of the file.
* @return The name of the class.
*/
protected String classFromFile (String file)
{
String filesep;
int index;
// remove any path
filesep = System.getProperty ("file.separator");
index = file.lastIndexOf (filesep);
if (-1 != index)
file = file.substring (index + filesep.length ());
// remove the extension
index = file.indexOf ('.');
if (-1 != index)
file = file.substring (0, index);
return (file);
}
/**
* Save the workspace contents to file.
* @param name The name of the file to save it under.
*/
public void save (String name)
{
Filter[] selections;
FilterBean bean;
StringBuffer buffer;
PrintWriter out;
String ok = "OK";
selections = getFilters ();
if (0 != selections.length)
{
bean = new FilterBean ();
bean.setURL (mURLField.getText ());
bean.setFilters (selections);
buffer = new StringBuffer ();
makeProgram (classFromFile (name), buffer, bean);
try
{
out = new PrintWriter (new FileWriter (name), true);
try
{
out.write (buffer.toString ());
out.flush ();
}
finally
{
out.close ();
}
}
catch (IOException ioe)
{
ioe.printStackTrace ();
}
}
else // ToDo: grey out save option if nothing to save...
JOptionPane.showOptionDialog (
mMainPanel,
"No filters to save.",
"Oops",
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE,
null,
new String[] { ok },
ok);
}
/**
* The action to take when "New" menu or button pressed.
*/
protected void newAction ()
{
mMainPanel.removeAll ();
mSelection.clear ();
relayout ();
}
/**
* The action to take when "Open" menu or button pressed.
*/
protected void openAction ()
{
FileDialog dialog;
File file;
dialog = new FileDialog (this);
dialog.setMode (FileDialog.LOAD);
dialog.setTitle ("Open");
dialog.setDirectory (mHomeDir);
dialog.setVisible (true);
if (null != dialog.getFile ())
{
mHomeDir = dialog.getDirectory ();
file = new File (mHomeDir + dialog.getFile ());
open (file.getAbsolutePath ());
setTitle (TITLE + " - " + file.getAbsolutePath ());
}
}
/**
* The action to take when "Save" menu or button pressed.
*/
protected void saveAction ()
{
String title;
int index;
File file;
FileDialog dialog;
title = getTitle ();
index = title.indexOf (" - ");
if (-1 != index)
file = new File (title.substring (index + 3));
else
{
dialog = new FileDialog (this);
dialog.setMode (FileDialog.SAVE);
dialog.setTitle ("Save");
dialog.setDirectory (mHomeDir);
dialog.setVisible (true);
if (null != dialog.getFile ())
{
mHomeDir = dialog.getDirectory ();
file = new File (mHomeDir + dialog.getFile ());
setTitle (TITLE + " - " + file.getAbsolutePath ());
}
else
file = null;
}
if (null != file)
save (file.getAbsolutePath ());
}
/**
* The action to take when "Save As" menu or button pressed.
*/
protected void saveasAction ()
{
setTitle (TITLE);
saveAction ();
}
/**
* The action to take when "Exit" menu or button pressed.
*/
protected void exitAction ()
{
exitApplication ();
}
/**
* The action to take when "Cut" menu or button pressed.
*/
protected void cutAction ()
{
String string;
StringSelection contents;
Clipboard cb;
// get the selection
string = serializeSelection ();
// copy to clipboard
contents = new StringSelection (string);
cb = Toolkit.getDefaultToolkit ().getSystemClipboard ();
cb.setContents (contents, this);
// delete the selection
deleteSelection ();
relayout ();
}
/**
* The action to take when "Copy" menu or button pressed.
*/
protected void copyAction ()
{
String string;
StringSelection contents;
Clipboard cb;
// get the selection
string = serializeSelection ();
// copy to clipboard
contents = new StringSelection (string);
cb = Toolkit.getDefaultToolkit ().getSystemClipboard ();
cb.setContents (contents, this);
}
/**
* The action to take when "Paste" menu or button pressed.
*/
protected void pasteAction ()
{
Clipboard cb;
Transferable content;
String string;
Filter[] filters;
Point point;
SubFilterList list;
// get the text
cb = Toolkit.getDefaultToolkit ().getSystemClipboard ();
content = cb.getContents (this);
if (content.isDataFlavorSupported (DataFlavor.stringFlavor))
{
try
{
string = (String)content.getTransferData (DataFlavor.stringFlavor);
// deserialize it and add into the selection
filters = Filter.reconstitute (string, new Parser (mURLField.getText ()));
// add it to the (single) selected object or main panel
if (isSingleSelection ()
&& (null != (list = getEnclosed (getSelection ()[0]))))
{
for (int i = 0; i < filters.length; i++)
list.addFilter (filters[i]);
}
else
{
point = new Point (0,0);
for (int i = 0; i < filters.length; i++)
{
filters[i].setLocation (point);
mMainPanel.add (filters[i]);
point.y += filters[i].getPreferredSize ().height;
}
}
setupMouseListeners (filters);
setupDropTargets (filters);
relayout ();
}
catch (Exception e)
{
e.printStackTrace ();
}
}
}
/**
* The action to take when "Delete" menu or button pressed.
*/
protected void deleteAction ()
{
// delete the selection
deleteSelection ();
relayout ();
}
/**
* The action to take when a filter menu or button pressed.
*/
protected void filterAction ()
{
String cls;
Filter filter;
SubFilterList list;
Point point;
// retrieve the source component placed there by actionPerformed
cls = mCurrentComponent.getName ();
filter = Filter.instantiate (cls);
// need this to get the underlying filter prepped?
try
{
filter = Filter.wrap (filter.getNodeFilter (), new Parser (mURLField.getText ()));
}
catch (ParserException pe)
{
pe.printStackTrace ();
}
// add it to the (single) selected object or main panel
if (isSingleSelection ()
&& (null != (list = getEnclosed (getSelection ()[0]))))
{
insertFilters (new Filter[] {filter}, null, list);
}
else
{
point = new Point (50,50); // find where and who to stick it into
insertFilters (new Filter[] {filter}, point, null);
}
}
/**
* The action to take when "Fetch" menu pressed.
*/
protected void fetchAction ()
{
JInternalFrame frame;
Dimension dimension;
Parser parser;
NodeList list;
// set up an internal frame for the results
frame = new JInternalFrame (mURLField.getText ());
frame.setClosable (true);
frame.setResizable (true);
dimension = mOutput.getSize ();
frame.setBounds (0, 0, dimension.width, dimension.height);
list = new NodeList ();
try
{
parser = new Parser (mURLField.getText ());
try
{
for (NodeIterator iterator = parser.elements (); iterator.hasMoreNodes (); )
list.add (iterator.nextNode ());
}
catch (EncodingChangeException ece)
{
list.removeAll ();
parser.reset ();
for (NodeIterator iterator = parser.elements (); iterator.hasMoreNodes (); )
list.add (iterator.nextNode ());
}
}
catch (ParserException pe)
{
pe.printStackTrace ();
}
JTree tree = new JTree (new HtmlTreeModel (list));
tree.setRootVisible (false);
tree.setCellRenderer (new HtmlTreeCellRenderer ());
JScrollPane treeView = new JScrollPane (tree);
frame.setContentPane (new JScrollPane (
treeView,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
mOutput.add (frame, new Integer (1));
try
{
frame.setSelected (true);
}
catch (PropertyVetoException pve)
{
pve.printStackTrace ();
}
frame.show ();
}
/**
* The action to take when "Execute" menu or button pressed.
*/
protected void executeAction ()
{
Filter[] selections;
FilterBean bean;
JInternalFrame frame;
Dimension dimension;
// JTextPane text;
selections = getSelection ();
if (0 == selections.length)
selections = getFilters ();
if (0 != selections.length)
{
bean = new FilterBean ();
bean.setURL (mURLField.getText ());
bean.setFilters (selections);
// set up an internal frame for the results
frame = new JInternalFrame (bean.getURL ());
frame.setClosable (true);
frame.setResizable (true);
dimension = mOutput.getSize ();
frame.setBounds (0, 0, dimension.width, dimension.height / 2);
JTree tree = new JTree (new HtmlTreeModel (bean.getNodes ()));
tree.setRootVisible (false);
tree.setCellRenderer (new HtmlTreeCellRenderer ());
JScrollPane treeView = new JScrollPane (tree);
frame.setContentPane (new JScrollPane (
treeView,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
// text = new JTextPane ();
// text.setText (bean.getNodes ().toHtml ());
// frame.setContentPane (new JScrollPane (
// text,
// ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
// ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
mOutput.add (frame, new Integer(2)); // layer 2?
try
{
frame.setSelected (true);
}
catch (PropertyVetoException pve)
{
pve.printStackTrace ();
}
frame.show ();
}
}
/**
* The action to take when "Instructions" menu pressed.
*/
protected void instructionsAction ()
{
String instructions =
"<html>" +
"Enter the target URL in the text box at the bottom of the window.<br>" +
"Choose 'Fetch Page' from the Operations menu to see the whole page.<br>" +
"Pick filters from the Filter menu or drag them from the toolbar.<br>" +
"Filters such as And, Or, Not, HasParent, HasChild and HasSibling contain other filters:<br>" +
"<ul><li>drag new filters into their blank areas at the bottom</li>" +
"<li>cut an existing filter and paste into a selected filter</li></ul>" +
"Build the filter incrementally, choosing 'Execute Filter' to test the selected filter.<br>" +
"Save creates a .java file that runs the top level filter.<br>" +
"Right click on a filter displays a pop-up menu.<br>" +
"Double click on a blue item in the result pane expands the tree." +
"</html>";
String close = "Close";
JOptionPane.showOptionDialog (
// not .showMessageDialog(
mMainPanel,
instructions,
"FilterBuilder Instructons",
// remove this:
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
// and remove rest of these:
null,
new String[] { close },
close);
}
/**
* The action to take when "Filtering" menu pressed.
*/
protected void filteringAction ()
{
String instructions =
"<html>" +
"The HTML Parser filter subsystem extracts items from a web page,<br>" +
"corresponding to the use-case 'I want this little piece of information from http://yadda'.<br>" +
"The web page is considered a heirarchical tree of nodes. Usually the root node is <html>,<br>" +
"intermediate level nodes are <div> and <table> for example,<br>" +
"and leaf nodes are things like text or <img>.<br>" +
"Any node that isn't the root node has a 'parent' node.<br>" +
"Leaf nodes, by definition, have no 'children'.<br>" +
"A filter is a Java class that answers the simple question:<br>" +
"<pre>Is this node acceptable? True or false.</pre><br>" +
"Some filters know the answer just by looking at the node,<br>" +
"while others must ask other filters, sometimes looking up or down the node heirarchy.<br>" +
"<b>The FilterBuilder is a program for making other programs that use filters.</b><br>" +
"By combining different types of filters, specific nodes can be isolated from the<br>" +
"target web page.<br>" +
"The results are usually passed on to another part of the users program<br>" +
"that does something useful with them.<br>" +
"The filters available include:<br>" +
"<ul>" +
"<li>AndFilter - The main 'combining' filter, answers <code>true</code> only if<br>" +
"all it's subfilters (predicates) are <code>true</code>.</li>" +
"<li>OrFilter - A 'combining' filter that answers <code>true</code> if<br>" +
"any of it's subfilters (predicates) are <code>true</code>.</li>" +
"<li>NotFilter - A 'reversing' filter that answers <code>true</code> if<br>" +
"it's subfilter (predicate) is <code>false</code>.</li>" +
"<li>StringFilter - A 'leaf' filter that answers <code>true</code> if<br>" +
"the node is text and it contains a certain sequence of characters.<br>" +
"It can be made case insensitive, but in this case a 'locale' must be<br>" +
"supplied as a context for upper-case conversion.</li>" +
"<li>RegexFilter - A 'leaf' filter that answers <code>true</code> if<br>" +
"the node is text and it contains a certain pattern (regular expression).<br>" +
"Regular expressions are descibed in the java.util.regex.Pattern class documentation.</li>" +
"<li>TagNameFilter - A filter that answers <code>true</code> if<br>" +
"the node is a tag and it has a certain name," +
"i.e. <div> would match the name <code>DIV</code>.</li>" +
"<li>NodeClassFilter - A filter that answers <code>true</code> if<br>" +
"the node is a certain tag class. Not recommended, use TagNameFilter instead.</li>" +
"<li>HasAttributeFilter - A filter that answers <code>true</code> if<br>" +
"the node is a tag and it has a certain attribute,<br>" +
"i.e. <script language=javascript> would match the attribute <code>LANGUAGE</code>.<br>" +
"It can be further restricted to have a certain attribute value as well,<br>" +
"i.e. 'javascript' in this example.</li>" +
"<li>HasParentFilter - A filter that answers <code>true</code> if<br>" +
"the node is a child of a node that is acceptable to a certain filter.<br>" +
"This can be made recursive, which means the acceptable parent can be<br>" +
"further up the heirarchy than just the immediate parent node.</li>" +
"<li>HasChildFilter - A filter that answers <code>true</code> if<br>" +
"the node is a parent of a node that is acceptable to a certain filter.<br>" +
"This can be made recursive, which means the acceptable child can be<br>" +
"further down the heirarchy than just the immediate children nodes.</li>" +
"<li>HasSiblingFilter - A filter that answers <code>true</code> if<br>" +
"the node is a sibling (they have a common parent) of a node that is<br>" +
"acceptable to a certain filter.</li>" +
"</ul>" +
"</html>";
String close = "Close";
JOptionPane.showOptionDialog (
// not .showMessageDialog(
mMainPanel,
instructions,
"FilterBuilder Instructons",
// remove this:
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
// and remove rest of these:
null,
new String[] { close },
close);
}
/**
* The action to take when "Tutorial" menu pressed.
*/
protected void tutorialAction ()
{
String instructions =
"<html>" +
"To get the title text from a page:<br>" +
"<ul><li>Choose 'New' from the File menu.</li>" +
"<li>Choose 'AndFilter' from the Filter menu.</li>" +
"<li>Select the And filter so it is highlighted.</li>" +
"<li>Choose 'HasParent' from the Filter menu.</li>" +
"<li>Toggle the 'Recursive' checkbox on in the HasParent filter.</li>" +
"<li>Select the HasParent filter so it is highlighted.</li>" +
"<li>Choose 'TagName' from the Filter menu.<br>" +
"<i>Alternatively, you can drag the TagName filter (icon Hello-BOB)<br>" +
"from the toolbar and drop inside the HasParent filter</i></li>" +
"<li>Choose 'TITLE' from the TagName combo-box.</li>" +
"<li>Select the And filter and choose 'Execute Filter' from the<br>" +
"Operations menu to test it.</li>" +
"<li>If there is unwanted non-text nodes in the result<br>" +
"select the And filter and choose 'RegexFilter' from the Filter menu.</li>" +
"<li>Test it again, as above.</li>" +
"<li>Choose 'Save' from the File menu and enter a filename like GetTitle.java</li>" +
"<li>Compile the java file and run it.</li></ul>" +
"</html>";
String close = "Close";
JOptionPane.showOptionDialog (
// not .showMessageDialog(
mMainPanel,
instructions,
"FilterBuilder Tutorial",
// remove this:
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
// and remove rest of these:
null,
new String[] { close },
close);
}
/**
* The action to take when "Hints" menu pressed.
*/
protected void hintsAction ()
{
String instructions =
"<html>" +
"Hints:<br>" +
"<ul><li>There is no undo yet, so save often.</li>" +
"<li>Recursive HasParent and HasChild filters can be costly.</li>" +
"<li>RegexFilter is more expensive than StringFilter.</li>" +
"<li>The order of predicates in And and Or filters matters for performance,<br>" +
"put cheap tests first.</li>" +
"<li>The same node may show up more than once in the results,<br>" +
"and at more than one nesting depth, depending on the filter used.</li>" +
"<li>Typing in a tag name in the TagName filter is not recommended,<br>" +
"since extraneous characters can be added. Use an item from the list instead.</li></ul>" +
"</html>";
String close = "Close";
JOptionPane.showOptionDialog (
// not .showMessageDialog(
mMainPanel,
instructions,
"FilterBuilder Hints",
// remove this:
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
// and remove rest of these:
null,
new String[] { close },
close);
}
/**
* The action to take when "About" menu or button pressed.
*/
protected void aboutAction ()
{
String close = "Close";
JOptionPane.showOptionDialog (
// not .showMessageDialog(
mMainPanel,
"<html><center><font color=black>The HTML Parser <font color=blue><b>FilterBuilder</b></font><br><i>by Derrick Oswald</i> <b>DerrickOswald@users.sourceforge.net</b><br>http://htmlparser.org<br><br><font size=-2>Copyright © 2005</font></center></html>",
"About FilterBuilder",
// remove this:
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
// and remove rest of these:
null,
new String[] { close },
close);
}
/**
* The action to take when "Expand" menu chosen.
*/
public void expandAction ()
{
setExpanded (getSelection (), true, false);
}
/**
* The action to take when "Collapse" menu chosen.
*/
public void collapseAction ()
{
setExpanded (getSelection (), false, false);
}
/**
* The action to take when "Expand All" menu chosen.
*/
public void expandAllAction ()
{
setExpanded (getSelection (), true, true);
}
/**
* The action to take when "Collapse" menu chosen.
*/
public void collapseAllAction ()
{
setExpanded (getSelection (), false, true);
}
/**
* Set up mouse listeners.
* Sets <code>this</code> up to listen to each command
* in the list as a MouseListener.
* Recursively descends the tree adding to all contained elements also.
* @param filters The container with commands in it.
*/
public void setupMouseListeners (Filter[] filters)
{
SubFilterList list;
for (int i = 0; i < filters.length; i++)
{
// set us up as a mouse listener on it
((Component)filters[i]).addMouseListener (this);
((Component)filters[i]).addMouseMotionListener (this);
list = getEnclosed (filters[i]);
if (null != list)
setupMouseListeners (list.getFilters ());
}
}
/**
* Set up drop targets.
* Recursively descends the filter tree and sets up
* the filter lists as drop targets.
* @param filters The container with filters in it.
*/
public void setupDropTargets (Filter[] filters)
{
SubFilterList list;
Component[] components;
for (int i = 0; i < filters.length; i++)
{
list = getEnclosed (filters[i]);
if (null != list)
{
components = list.getDropTargets ();
for (int j = 0; j < components.length; j++)
new DropTarget (components[j], this);
setupDropTargets (list.getFilters ());
}
}
}
/**
* Expand or collapse filters, possibly recursively.
* @param filters The list of filters to expand or collapse.
* @param expanded If <code>true</code> the filters are expanded,
* otherwise they are collapsed.
* @param recursive If <code>true</code> the filters are processed
* recursively.
*/
public void setExpanded (
Filter[] filters,
boolean expanded,
boolean recursive)
{
SubFilterList list;
for (int i = 0; i < filters.length; i++)
{
if (recursive && (null != (list = getEnclosed (filters[i]))))
setExpanded (list.getFilters (), expanded, recursive);
filters[i].setExpanded (expanded);
}
}
/**
* Retrieve the top level filters in the main window.
* @return The top level filters.
*/
public Filter[] getFilters ()
{
Component[] components;
Filter[] ret;
components = mMainPanel.getComponents ();
ret = new Filter[components.length];
System.arraycopy (components, 0, ret, 0, components.length);
return (ret);
}
/**
* Redo the layout.
*/
public void relayout ()
{
mMainPanel.invalidate ();
mMainScroller.invalidate ();
mMainScroller.validate ();
mMainScroller.repaint ();
}
/**
* Read a workspace from file.
* The current contents are erased.
* @param name The name of the file to open.
*/
public void open (String name)
{
LineNumberReader reader;
String line;
Filter[] filters;
Point point;
Dimension dimension;
try
{
reader = new LineNumberReader (new FileReader (name));
while (null != (line = reader.readLine ()))
if (line.startsWith ("// ["))
{
line = line.substring (3);
try
{
filters = Filter.reconstitute (line, new Parser (mURLField.getText ()));
mMainPanel.removeAll ();
point = new Point ();
for (int i = 0; i < filters.length; i++)
{
dimension = filters[i].getPreferredSize ();
mMainPanel.add (filters[i]);
filters[i].setLocation (point);
point.y += dimension.height;
}
setupMouseListeners (filters);
setupDropTargets (filters);
relayout ();
}
catch (ParserException pe)
{
pe.printStackTrace ();
}
break;
}
reader.close ();
}
catch (IOException ioe)
{
ioe.printStackTrace ();
}
}
/**
* Exit back to the operating system.
*/
void exitApplication ()
{
this.setVisible (false); // hide the Frame
this.dispose (); // free the system resources
System.exit (0); // close the application
}
/**
* Show a pop up context menu.
* Shows a context sensitive popup menu at the location of the
* mouse event.
* @param event The mouse event that initiates the popup.
*/
public void showContextMenu (MouseEvent event)
{
JPopupMenu menu;
JMenuItem item;
menu = new JPopupMenu ();
menu.setName ("Popup");
item = new JMenuItem ("Expand");
item.setActionCommand ("expandAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Collapse");
item.setActionCommand ("collapseAction");
item.addActionListener (this);
menu.add (item);
menu.addSeparator ();
item = new JMenuItem ("Expand All");
item.setActionCommand ("expandAllAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("CollapseAll");
item.setActionCommand ("collapseAllAction");
item.addActionListener (this);
menu.add (item);
menu.addSeparator ();
item = new JMenuItem ("Cut");
item.setActionCommand ("cutAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Copy");
item.setActionCommand ("copyAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Paste");
item.setActionCommand ("pasteAction");
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Delete");
item.setActionCommand ("deleteAction");
item.addActionListener (this);
menu.add (item);
menu.addSeparator ();
item = new JMenuItem ("Execute Filter");
item.setActionCommand ("executeAction");
item.addActionListener (this);
menu.add (item);
menu.show (event.getComponent (), event.getX (), event.getY ());
}
//
// selection manipulation
//
/**
* Add a filter to the current selection set.
* @param filter The filter to add.
*/
protected void addSelection (Filter filter)
{
if (!selectionContains (filter))
mSelection.addElement (filter);
filter.setSelected (true);
mMoved = false;
}
/**
* Remove a filter from the current selection set.
* @param filter The filter to remove.
*/
protected void removeSelection (Filter filter)
{
mSelection.removeElement (filter); // no harm if not contained
filter.setSelected (false);
}
/**
* Select(highlight)/deselect the current selection set.
* @param select If <code>true</code> turn on highlighting,
* turn it off otherwise.
*/
protected void selectSelection (boolean select)
{
int count;
Filter filter;
count = mSelection.size ();
for (int i = 0; i < count; i++)
{
filter = (Filter)mSelection.elementAt (i);
filter.setSelected (select);
}
}
/**
* Clear (empty) the current selection set.
*/
protected void clearSelection ()
{
selectSelection (false);
mSelection.removeAllElements ();
}
/**
* Move the current selection set as a group.
* @param translation The displacement to move them all by.
*/
protected void moveSelection (Point translation)
{
int count;
Filter filter;
Point point;
count = mSelection.size ();
for (int i = 0; i < count; i++)
{
filter = (Filter)mSelection.elementAt (i);
point = filter.getLocation ();
point.translate (translation.x, translation.y);
synchronized (filter.getTreeLock ())
{
filter.setLocation (point.x, point.y);
}
}
mMoved = true;
}
/**
* Check if the current selection set contains the given filter.
* @param filter The filter to check.
* @return <code>true</code> if the filter is a member,
* <code>false</code> otherwise.
*/
protected boolean selectionContains (Filter filter)
{
return (mSelection.contains (filter));
}
/**
* Return the last filter added to the selection set.
* @return The last filter added or <code>null</code> if the current
* selection set is empty.
*/
protected Filter lastSelected ()
{
Filter ret;
ret = null;
if (0 < mSelection.size ())
ret = (Filter)mSelection.lastElement ();
return (ret);
}
/**
* Return the current selection set as an array.
* @return The array of selected filters.
*/
protected Filter[] getSelection ()
{
Filter[] ret;
ret = new Filter[mSelection.size ()];
mSelection.copyInto (ret);
return (ret);
}
/**
* Serialize the current selection set.
* @return The serialized form of the set of filters.
*/
public String serializeSelection ()
{
Filter[] filters;
StringWriter writer;
PrintWriter out;
filters = getSelection ();
writer = new StringWriter (200);
out = new PrintWriter (writer, false);
try
{
out.println (Filter.deconstitute (filters));
}
catch (IOException ioe)
{
ioe.printStackTrace ();
}
finally
{
out.close ();
}
return (writer.getBuffer ().toString ());
}
/**
* Delete the current selection set from the filters in the GUI.
*/
public void deleteSelection ()
{
Filter[] filters;
SubFilterList list;
filters = getSelection ();
for (int i = 0; i < filters.length; i++)
{
list = getEnclosing (filters[i]);
if (null != list)
list.removeFilter (filters[i]);
else
mMainPanel.remove (filters[i]);
}
mSelection.clear ();
}
/**
* Check if there is more than one filter selected.
* @return <code>true</code> if only one filter is selected,
* <code>false</code> otherwise.
*/
public boolean isSingleSelection ()
{
return (1 == mSelection.size());
}
//
// MouseListener interface
//
/**
* Invoked when the mouse has been clicked on a component.
* @param event The mouse clicked event.
*/
public void mouseClicked (MouseEvent event)
{
Object component;
Filter filter;
SubFilterList list;
int modifiers;
boolean contained;
Filter[] filters;
component = event.getSource ();
if (component instanceof Filter)
{
filter = (Filter)component;
modifiers = event.getModifiers ();
contained = selectionContains (filter);
if (0 != (modifiers & InputEvent.SHIFT_MASK))
{
// add everything from last selected to this command
list = getEnclosed (filter);
if (null != list)
filters = list.getFilters ();
else
filters = getFilters ();
Filter last = lastSelected ();
if (null == last)
addSelection (filter);
else
{
int current = -1;
int recent = -1;
for (int i = 0; i < filters.length; i++)
{
if (filters[i] == filter)
current = i;
if (filters[i] == last)
recent = i;
}
if ((current != -1) && (recent != -1))
for (int i = Math.min (current, recent);
i <= Math.max (current, recent); i++)
addSelection (filters[i]);
}
}
else if (0 != (modifiers & InputEvent.CTRL_MASK))
{
// add just the new command
if (contained)
removeSelection (filter);
else
addSelection (filter);
}
else if (0 != (modifiers & InputEvent.BUTTON3_MASK))
{
if (!selectionContains (filter))
{
clearSelection ();
addSelection (filter);
}
showContextMenu (event);
}
else
{
clearSelection ();
addSelection (filter);
}
}
else
clearSelection ();
}
/**
* Invoked when a mouse button has been released on a component.
* @param event The mouse released event.
*/
public void mouseReleased (MouseEvent event)
{
}
/**
* Invoked when the mouse enters a component.
* @param event The mouse entered event.
*/
public void mouseEntered (MouseEvent event)
{
}
/**
* Invoked when the mouse exits a component.
* @param event The mouse exited event.
*/
public void mouseExited (MouseEvent event)
{
}
/**
* Invoked when a mouse button has been pressed on a component.
* @param event The mouse pressed event.
*/
public void mousePressed (MouseEvent event)
{
Object component;
Point newpoint;
Point upperleft;
component = event.getSource ();
if (component instanceof Filter)
{
// translate the point relative to the enclosing container
newpoint = event.getPoint ();
upperleft = ((Component)component).getLocation ();
newpoint.translate (upperleft.x, upperleft.y);
setBasePoint (newpoint);
}
else
setBasePoint (null);
}
//
// MouseMotionListener interface
//
/**
* Mouse drag notification.
* Invoked when a mouse button is pressed on a component and
* then dragged. Mouse drag events will continue to be
* delivered to the component where the first originated
* until the mouse button is released (regardless of whether
* the mouse position is within the bounds of the component).
* @param event The mouse drag event.
*/
public synchronized void mouseDragged (MouseEvent event)
{
Object component;
Filter filter;
Point base;
Point newpoint;
Point upperleft;
Point translation;
component = event.getSource ();
if (component instanceof Filter)
{
filter = (Filter)component;
if (selectionContains (filter)) // drag on a selected item
{
if (null == getEnclosing (filter)) // not contained
try
{
base = getBasePoint ();
if (null != base)
{
newpoint = event.getPoint ();
// translate the point relative to the enclosing container
upperleft = filter.getLocation ();
newpoint.translate (upperleft.x, upperleft.y);
// get the difference between this point and the old base
translation = new Point (
newpoint.x - base.x,
newpoint.y - base.y);
// update the base point
setBasePoint (newpoint);
// apply this difference to the selection
moveSelection (translation);
}
}
catch (Exception e)
{
}
}
else
mouseClicked (event); // a small slip shouldn't stop a click
}
}
/**
* Mouse move notification.
* Invoked when the mouse button has been moved on a component
* (with no buttons no down).
* @param event The mouse moved event.
*/
public void mouseMoved (MouseEvent event)
{
}
//
// WindowListener interface
//
/**
* Invoked the first time a window is made visible.
* <i>Not used.</i>
* @param event The window event.
*/
public void windowOpened (WindowEvent event) {}
/**
* Handles window closing event.
* Performs function <code>exitApplication()</code>.
* @param event The window event.
*/
public void windowClosing (WindowEvent event)
{
if (event.getSource () == this)
exitApplication ();
}
/**
* Invoked when a window has been closed as the result
* of calling dispose on the window.
* <i>Not used.</i>
* @param event The window event.
*/
public void windowClosed (WindowEvent event) {}
/**
* Invoked when a window is changed from a normal to a
* minimized state. For many platforms, a minimized window
* is displayed as the icon specified in the window's
* iconImage property.
* <i>Not used.</i>
* @param event The window event.
*/
public void windowIconified (WindowEvent event) {}
/**
* Invoked when a window is changed from a minimized
* to a normal state.
* <i>Not used.</i>
* @param event The window event.
*/
public void windowDeiconified (WindowEvent event) {}
/**
* Invoked when the window is set to be the user's
* active window, which means the window (or one of its
* subcomponents) will receive keyboard events.
* <i>Not used.</i>
* @param event The window event.
*/
public void windowActivated (WindowEvent event) {}
/**
* Invoked when a window is no longer the user's active
* window, which means that keyboard events will no longer
* be delivered to the window or its subcomponents.
* <i>Not used.</i>
* @param event The window event.
*/
public void windowDeactivated (WindowEvent event) {}
//
// ActionListener interface
//
/**
* Handles menu and toolbar item choices.
* @param event The action even that triggers this function.
*/
public void actionPerformed (ActionEvent event)
{
Object object;
String action;
object = event.getSource();
// if (object instanceof JButton)
// {
// String url;
// url = mURLField.getText ();
// mURLField.selectAll ();
// //setURL (url);
// }
if (object instanceof JButton)
action = ((JButton)object).getActionCommand ();
else if (object instanceof JMenuItem)
action = ((JMenuItem)object).getActionCommand ();
else
action = null;
if (object instanceof Component)
mCurrentComponent = (Component)object;
if (null != action)
try
{
Method method = this.getClass ().getDeclaredMethod (action, new Class[0]);
method.invoke (this, new Object[0]);
}
catch (NoSuchMethodException nsme)
{
System.out.println ("no " + action + " method found");
}
catch (Exception e)
{
e.printStackTrace ();
}
}
//
// ClipboardOwner interface
//
/**
* Notifies this object that it is no longer the owner
* of the contents of the clipboard.
* @param clipboard The clipboard that is no longer owned.
* @param contents The contents which this owner had placed on the clipboard.
*/
public void lostOwnership (Clipboard clipboard, Transferable contents)
{
System.out.println ("lost clipboard ownership");
}
//
// DragGestureListener interface
//
/**
* A DragGestureRecognizer has detected a platform-dependent drag initiating gesture.
* It is notifying this listener in order for it to initiate the action for the user.
* @param event The DragGestureEvent describing the gesture that has just occurred.
*/
public void dragGestureRecognized (DragGestureEvent event)
{
Component component;
String cls;
Filter filter;
StringSelection text;
component = event.getComponent ();
try
{
cls = component.getName (); // (String)Filter.mWrappers.get (component.getName ());
if (null != cls)
{
filter = Filter.instantiate (cls);
text = new StringSelection (Filter.deconstitute (new Filter[] { filter }));
mDragSource.startDrag (event, DragSource.DefaultMoveDrop, text, this);
}
}
catch (Exception e)
{
e.printStackTrace ();
}
}
//
// DragSourceListener interface
//
/**
* This message goes to DragSourceListener,
* informing it that the dragging has ended.
* @param event Details about the drop event.
*/
public void dragDropEnd (DragSourceDropEvent event)
{
if (event.getDropSuccess ())
{
// System.out.println ("added new class");
}
}
/**
* This message goes to DragSourceListener,
* informing it that the dragging has entered the DropSite.
* @param event Details about the drag event.
*/
public void dragEnter (DragSourceDragEvent event)
{
// System.out.println ("dragEnter");
}
/**
* This message goes to DragSourceListener, informing it that the dragging
* has exited the DropSite.
* @param event Details about the drag event.
*/
public void dragExit (DragSourceEvent event)
{
// System.out.println( "dragExit");
}
/**
* This message goes to DragSourceListener, informing it that the dragging is currently
* ocurring over the DropSite.
* @param event Details about the drag event.
*/
public void dragOver (DragSourceDragEvent event)
{
// System.out.println( "dragExit");
}
/**
* This is invoked when the user changes the dropAction.
* @param event Details about the drop action event.
*/
public void dropActionChanged (DragSourceDragEvent event)
{
// System.out.println( "dropActionChanged");
}
//
// DropTargetListener interface
//
/**
* This is invoked when you are dragging over the DropSite.
* @param event Details about the drag event.
*/
public void dragEnter (DropTargetDragEvent event)
{
// debug messages for diagnostics
// event.acceptDrag (DnDConstants.ACTION_MOVE);
// System.out.println ("dragEnter");
DropTargetContext context;
Component component;
SubFilterList list;
// find the enclosing filter
context = event.getDropTargetContext ();
component = context.getComponent ();
while ( (null != component)
&& !(component instanceof SubFilterList)
&& !(component == mMainPanel))
component = component.getParent ();
if (component instanceof SubFilterList)
list = (SubFilterList)component;
else
list = null;
// so either list is the enclosing list,
// or list is null and the target component is the main panel
if (null != list)
if (!list.canAccept ())
event.rejectDrag ();
else
list.setSelected (true);
}
/**
* Thi ss invoked when you are exit the DropSite without dropping.
* @param event Details about the drag event.
*/
public void dragExit (DropTargetEvent event)
{
// debug messages for diagnostics
// event.acceptDrag (DnDConstants.ACTION_MOVE);
// System.out.println ("dragEnter");
DropTargetContext context;
Component component;
SubFilterList list;
// find the enclosing filter
context = event.getDropTargetContext ();
component = context.getComponent ();
while ( (null != component)
&& !(component instanceof SubFilterList)
&& !(component == mMainPanel))
component = component.getParent ();
if (component instanceof SubFilterList)
list = (SubFilterList)component;
else
list = null;
// so either list is the enclosing list,
// or list is null and the target component is the main panel
if (null != list)
list.setSelected (false);
}
/**
* This is invoked when a drag operation is going on.
* @param event Details about the drag event.
*/
public void dragOver (DropTargetDragEvent event)
{
// System.out.println( "dragOver");
}
/**
* This is invoked when a drop has occurred.
* @param event The drop event.
*/
public void drop (DropTargetDropEvent event)
{
DropTargetContext context;
Component component;
SubFilterList list;
String s;
Point point;
Filter[] filters;
boolean accept;
// find the enclosing filter
context = event.getDropTargetContext ();
component = context.getComponent ();
while ( (null != component)
&& !(component instanceof SubFilterList)
&& !(component == mMainPanel))
component = component.getParent ();
if (component instanceof SubFilterList)
list = (SubFilterList)component;
else
list = null;
// so either list is the enclosing list,
// or list is null and the target component is the main panel
try
{
accept = false;
Transferable transferable = event.getTransferable();
// we accept only Strings
if (transferable.isDataFlavorSupported (DataFlavor.stringFlavor))
{
accept = true;
event.acceptDrop (DnDConstants.ACTION_MOVE);
s = (String)transferable.getTransferData (DataFlavor.stringFlavor);
point = event.getLocation ();
try
{
// get the filter and add into the target
filters = Filter.reconstitute (s, new Parser (mURLField.getText ()));
if (0 < filters.length)
insertFilters (filters, point, list);
if (null != list)
list.setSelected (false);
}
catch (Exception e)
{
e.printStackTrace ();
}
// signal the drop was successful
context.dropComplete (accept);
}
else
event.rejectDrop();
}
catch (IOException exception)
{
exception.printStackTrace();
System.err.println( "Exception" + exception.getMessage());
event.rejectDrop();
}
catch (UnsupportedFlavorException ufException)
{
ufException.printStackTrace();
System.err.println( "Exception" + ufException.getMessage());
event.rejectDrop();
}
}
/**
* This is invoked if the user modifies the current drop gesture.
* @param event Details about the drop action change event.
*/
public void dropActionChanged (DropTargetDragEvent event)
{
// System.out.println( "dropActionChanged");
}
/**
* The entry point for this application.
* Creates a new FilterBuilder and makes it visible.
* @param args [0] optional URL to operate on.
*/
public static void main (String args[])
{
try
{
// set the Look and Feel to the the native system
// try
// {
// javax.swing.UIManager.setLookAndFeel (javax.swing.UIManager.getSystemLookAndFeelClassName ());
// }
// catch (Exception e)
// {
// }
// create a new instance of our application's frame, and make it visible
FilterBuilder builder = new FilterBuilder ();
if (0 != args.length)
builder.mURLField.setText (args[0]);
builder.setVisible (true);
}
catch (Throwable t)
{
t.printStackTrace ();
// ensure the application exits with an error condition
System.exit (1);
}
}
}