/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * KnowledgeFlow.java * Copyright (C) 2002 Mark Hall * */ package weka.gui.beans; import weka.core.Utils; import weka.gui.ListSelectorDialog; import weka.gui.LogPanel; import java.io.OutputStream; import java.io.InputStream; import java.io.FileOutputStream; import java.io.FileInputStream; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.File; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Vector; import java.util.Properties; import java.util.Enumeration; import java.util.Date; import java.text.SimpleDateFormat; import java.beans.Customizer; import java.beans.EventSetDescriptor; import java.beans.Beans; import java.beans.PropertyDescriptor; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JButton; import javax.swing.JToggleButton; import javax.swing.JButton; import javax.swing.DefaultListModel; import javax.swing.JList; import javax.swing.JLabel; import javax.swing.JComponent; import javax.swing.JPopupMenu; import javax.swing.JMenuItem; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.JScrollPane; import javax.swing.ButtonGroup; import javax.swing.ButtonModel; import javax.swing.ImageIcon; import javax.swing.SwingConstants; import javax.swing.JFileChooser; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Dimension; import java.awt.event.MouseEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; import java.awt.Point; import java.awt.Font; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.Image; import java.awt.Toolkit; import java.awt.Insets; import java.awt.Component; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.IntrospectionException; /** * Main GUI class for the KnowledgeFlow * * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a> * @version $Revision: 1.1.1.1 $ * @since 1.0 * @see JPanel * @see PropertyChangeListener */ public class KnowledgeFlow extends JPanel implements PropertyChangeListener { /** * Location of the property file for the KnowledgeFlow */ protected static String PROPERTY_FILE = "weka/gui/beans/Beans.props"; /** Contains the editor properties */ private static Properties BEAN_PROPERTIES; /** * Holds the details needed to construct button bars for various supported * classes of weka algorithms/tools */ private static Vector TOOLBARS = new Vector(); /** Loads the configuration property file */ static { // Allow a properties file in the current directory to override try { BEAN_PROPERTIES = Utils.readProperties(PROPERTY_FILE); java.util.Enumeration keys = (java.util.Enumeration)BEAN_PROPERTIES.propertyNames(); if (!keys.hasMoreElements()) { throw new Exception( "Could not read a configuration file for the bean\n" +"panel. An example file is included with the Weka distribution.\n" +"This file should be named \"" + PROPERTY_FILE + "\" and\n" +"should be placed either in your user home (which is set\n" + "to \"" + System.getProperties().getProperty("user.home") + "\")\n" + "or the directory that java was started from\n"); } String standardToolBarNames = BEAN_PROPERTIES. getProperty("weka.gui.beans.KnowledgeFlow.standardToolBars"); StringTokenizer st = new StringTokenizer(standardToolBarNames, ", "); while (st.hasMoreTokens()) { String tempBarName = st.nextToken().trim(); // construct details for this toolbar Vector newV = new Vector(); // add the name of the toolbar newV.addElement(tempBarName); // indicate that this is a standard toolbar (no wrapper bean) newV.addElement("null"); String toolBarContents = BEAN_PROPERTIES. getProperty("weka.gui.beans.KnowledgeFlow."+tempBarName); StringTokenizer st2 = new StringTokenizer(toolBarContents, ", "); while (st2.hasMoreTokens()) { String tempBeanName = st2.nextToken().trim(); newV.addElement(tempBeanName); } TOOLBARS.addElement(newV); } } catch (Exception ex) { JOptionPane.showMessageDialog(null, ex.getMessage(), "KnowledgeFlow", JOptionPane.ERROR_MESSAGE); } try { /* now process the keys in the GenericObjectEditor.props. For each key that has an entry in the Beans.props associating it with a bean component a button tool bar will be created */ Properties GEOProps = Utils.readProperties("weka/gui/GenericObjectEditor.props"); Enumeration en = GEOProps.propertyNames(); while (en.hasMoreElements()) { String geoKey = (String)en.nextElement(); // try to match this key with one in the Beans.props file String beanCompName = BEAN_PROPERTIES.getProperty(geoKey); if (beanCompName != null) { // add details necessary to construct a button bar for this class // of algorithms Vector newV = new Vector(); // check for a naming alias for this toolbar String toolBarNameAlias = BEAN_PROPERTIES.getProperty(geoKey+".alias"); String toolBarName = (toolBarNameAlias != null) ? toolBarNameAlias : geoKey.substring(geoKey.lastIndexOf('.')+1, geoKey.length()); // Name for the toolbar (name of weka algorithm class) newV.addElement(toolBarName); // Name of bean capable of handling this class of algorithm newV.addElement(beanCompName); // add the root package for this key String rootPackage = geoKey.substring(0, geoKey.lastIndexOf('.')); newV.addElement(rootPackage); // All the weka algorithms of this class of algorithm String wekaAlgs = GEOProps.getProperty(geoKey); //------ test the HierarchyPropertyParser weka.gui.HierarchyPropertyParser hpp = new weka.gui.HierarchyPropertyParser(); hpp.build(wekaAlgs, ", "); // System.err.println(hpp.showTree()); // ----- end test the HierarchyPropertyParser newV.addElement(hpp); // add the hierarchical property parser StringTokenizer st = new StringTokenizer(wekaAlgs, ", "); while (st.hasMoreTokens()) { String current = st.nextToken().trim(); newV.addElement(current); } TOOLBARS.addElement(newV); } } } catch (Exception ex) { JOptionPane.showMessageDialog(null, "Could not read a configuration file for the generic objecte editor" +". An example file is included with the Weka distribution.\n" +"This file should be named \"GenericObjectEditor.props\" and\n" +"should be placed either in your user home (which is set\n" + "to \"" + System.getProperties().getProperty("user.home") + "\")\n" + "or the directory that java was started from\n", "KnowledgeFlow", JOptionPane.ERROR_MESSAGE); } } /** * Used for displaying the bean components and their visible * connections * * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a> * @version $Revision: 1.1.1.1 $ * @since 1.0 * @see JPanel */ protected class BeanLayout extends JPanel { public void paintComponent(Graphics gx) { super.paintComponent(gx); BeanInstance.paintLabels(gx); BeanConnection.paintConnections(gx); // BeanInstance.paintConnections(gx); } public void doLayout() { super.doLayout(); Vector comps = BeanInstance.getBeanInstances(); for (int i = 0; i < comps.size(); i++) { BeanInstance bi = (BeanInstance)comps.elementAt(i); JComponent c = (JComponent)bi.getBean(); Dimension d = c.getPreferredSize(); c.setBounds(bi.getX(), bi.getY(), d.width, d.height); c.revalidate(); } } } // constants for operations in progress protected static final int NONE = 0; protected static final int MOVING = 1; protected static final int CONNECTING = 2; protected static final int ADDING = 3; // which operation is in progress private int m_mode = NONE; /** * Button group to manage all toolbar buttons */ private ButtonGroup m_toolBarGroup = new ButtonGroup(); /** * Holds the selected toolbar bean */ private Object m_toolBarBean; /** * The layout area */ private BeanLayout m_beanLayout = new BeanLayout(); /** * Tabbed pane to hold tool bars */ private JTabbedPane m_toolBars = new JTabbedPane(); private JToggleButton m_pointerB; private JButton m_saveB; private JButton m_loadB; private JButton m_stopB; /** * Reference to bean being manipulated */ private BeanInstance m_editElement; /** * Event set descriptor for the bean being manipulated */ private EventSetDescriptor m_sourceEventSetDescriptor; /** * Used to record screen coordinates during move and connect * operations */ private int m_oldX, m_oldY; private int m_startX, m_startY; /** The file chooser for selecting layout files */ protected JFileChooser m_FileChooser = new JFileChooser(new File(System.getProperty("user.dir"))); protected LogPanel m_logPanel = new LogPanel(); /** * Creates a new <code>KnowledgeFlow</code> instance. */ public KnowledgeFlow() { m_beanLayout.setLayout(null); // handle mouse events m_beanLayout.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me) { if (m_toolBarBean == null) { if (((me.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) && m_mode == NONE) { BeanInstance bi = BeanInstance.findInstance(me.getPoint()); JComponent bc = null; if (bi != null) { bc = (JComponent)(bi.getBean()); } if (bc != null && (bc instanceof Visible)) { m_editElement = bi; m_oldX = me.getX(); m_oldY = me.getY(); m_mode = MOVING; } } } } public void mouseReleased(MouseEvent me) { if (m_editElement != null && m_mode == MOVING) { m_editElement = null; revalidate(); m_beanLayout.repaint(); m_mode = NONE; } } public void mouseClicked(MouseEvent me) { BeanInstance bi = BeanInstance.findInstance(me.getPoint()); if (m_mode == ADDING || m_mode == NONE) { // try and popup a context sensitive menu if we have // been clicked over a bean. if (bi != null) { JComponent bc = (JComponent)bi.getBean(); if ((me.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK) { doPopup(me.getPoint(), bi, me.getX(), me.getY()); } } else { if ((me.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK) { // find connections if any close to this point int delta = 10; deleteConnectionPopup(BeanConnection. getClosestConnections(new Point(me.getX(), me.getY()), delta), me.getX(), me.getY()); } else if (m_toolBarBean != null) { // otherwise, if a toolbar button is active then // add the component addComponent(me.getX(), me.getY()); } } } if (m_mode == CONNECTING) { // undraw rubberbanding line and turn off connecting points Vector beanInstances = BeanInstance.getBeanInstances(); for (int i = 0; i < beanInstances.size(); i++) { JComponent bean = (JComponent)((BeanInstance)beanInstances.elementAt(i)). getBean(); if (bean instanceof Visible) { ((Visible)bean).getVisual().setDisplayConnectors(false); } } Graphics2D gx = (Graphics2D)m_beanLayout.getGraphics(); gx.setXORMode(java.awt.Color.white); // remove the old rubberbanded line gx.drawLine(m_startX, m_startY, m_oldX, m_oldY); if (bi != null) { boolean doConnection = false; if (!(bi.getBean() instanceof BeanCommon)) { doConnection = true; } else { // Give the target bean a chance to veto the proposed // connection if (((BeanCommon)bi.getBean()). connectionAllowed(m_sourceEventSetDescriptor.getName())) { doConnection = true; } } if (doConnection) { // attempt to connect source and target beans BeanConnection bc = new BeanConnection(m_editElement, bi, m_sourceEventSetDescriptor); } m_beanLayout.repaint(); } gx.dispose(); m_mode = NONE; m_editElement = null; m_sourceEventSetDescriptor = null; } } }); m_beanLayout.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent me) { if (m_editElement != null && m_mode == MOVING) { ImageIcon ic = ((Visible)m_editElement.getBean()). getVisual().getStaticIcon(); int width = ic.getIconWidth() / 2; int height = ic.getIconHeight() / 2; m_editElement.setX(m_oldX-width); m_editElement.setY(m_oldY-height); m_beanLayout.repaint(); // note the new points m_oldX = me.getX(); m_oldY = me.getY(); } } public void mouseMoved(MouseEvent e) { if (m_mode == CONNECTING) { Graphics2D gx = (Graphics2D)m_beanLayout.getGraphics(); gx.setXORMode(java.awt.Color.white); // remove the old rubberbanded line gx.drawLine(m_startX, m_startY, m_oldX, m_oldY); // note the new coordinates m_oldX = e.getX(); m_oldY = e.getY(); // draw the new rubberbanded line gx.drawLine(m_startX, m_startY, m_oldX, m_oldY); gx.dispose(); } } }); String date = (new SimpleDateFormat("EEEE, d MMMM yyyy")) .format(new Date()); m_logPanel.logMessage("Weka Knowledge Flow was written by Mark Hall"); m_logPanel.logMessage("Weka Knowledge Flow"); m_logPanel.logMessage("(c) 2002-2003 Mark Hall"); m_logPanel.logMessage("web: http://www.cs.waikato.ac.nz/~ml/"); m_logPanel.logMessage( date); m_logPanel.statusMessage("Welcome to the Weka Knowledge Flow"); JPanel p1 = new JPanel(); p1.setLayout(new BorderLayout()); p1.setBorder(javax.swing.BorderFactory.createCompoundBorder( javax.swing.BorderFactory. createTitledBorder("Knowledge Flow Layout"), javax.swing.BorderFactory.createEmptyBorder(0, 5, 5, 5) )); final JScrollPane js = new JScrollPane(m_beanLayout); p1.add(js, BorderLayout.CENTER); setLayout(new BorderLayout()); add(p1, BorderLayout.CENTER); m_beanLayout.setSize(1024, 768); Dimension d = m_beanLayout.getPreferredSize(); m_beanLayout.setMinimumSize(d); m_beanLayout.setMaximumSize(d); m_beanLayout.setPreferredSize(d); add(m_logPanel, BorderLayout.SOUTH); setUpToolBars(); } private Image loadImage(String path) { Image pic = null; java.net.URL imageURL = ClassLoader.getSystemResource(path); if (imageURL == null) { // System.err.println("Warning: unable to load "+path); } else { pic = Toolkit.getDefaultToolkit(). getImage(imageURL); } return pic; } /** * Describe <code>setUpToolBars</code> method here. */ private void setUpToolBars() { JPanel toolBarPanel = new JPanel(); toolBarPanel.setLayout(new BorderLayout()); // first construct the toolbar for saving, loading etc JToolBar fixedTools = new JToolBar(); fixedTools.setOrientation(JToolBar.VERTICAL); m_saveB = new JButton(new ImageIcon(loadImage(BeanVisual.ICON_PATH +"Save24.gif"))); m_saveB.setToolTipText("Save layout"); m_loadB = new JButton(new ImageIcon(loadImage(BeanVisual.ICON_PATH +"Open24.gif"))); m_stopB = new JButton(new ImageIcon(loadImage(BeanVisual.ICON_PATH +"Stop24.gif"))); m_stopB.setToolTipText("Stop all execution"); m_loadB.setToolTipText("Load layout"); Image tempI = loadImage(BeanVisual.ICON_PATH+"Pointer.gif"); m_pointerB = new JToggleButton(new ImageIcon(tempI)); m_pointerB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { m_toolBarBean = null; m_mode = NONE; setCursor(Cursor. getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }); m_toolBarGroup.add(m_pointerB); fixedTools.add(m_pointerB); fixedTools.add(m_saveB); fixedTools.add(m_loadB); fixedTools.add(m_stopB); Dimension dP = m_saveB.getPreferredSize(); Dimension dM = m_saveB.getMaximumSize(); fixedTools.setFloatable(false); m_pointerB.setPreferredSize(dP); m_pointerB.setMaximumSize(dM); toolBarPanel.add(fixedTools, BorderLayout.WEST); m_saveB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { saveLayout(); } }); m_loadB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { loadLayout(); } }); m_stopB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Vector components = BeanInstance.getBeanInstances(); for (int i = 0; i < components.size(); i++) { Object temp = ((BeanInstance)components.elementAt(i)).getBean(); if (temp instanceof BeanCommon) { ((BeanCommon)temp).stop(); } } } }); final int STANDARD_TOOLBAR = 0; final int WEKAWRAPPER_TOOLBAR = 1; int toolBarType = STANDARD_TOOLBAR; // set up wrapper toolbars for (int i = 0; i < TOOLBARS.size(); i++) { Vector tempBarSpecs = (Vector)TOOLBARS.elementAt(i); // name for the tool bar String tempBarName = (String)tempBarSpecs.elementAt(0); // name of the bean component to handle this class of weka algorithms String tempBeanCompName = (String)tempBarSpecs.elementAt(1); // a JPanel holding an instantiated bean + label ready to be added // to the current toolbar JPanel tempBean; // the root package for weka algorithms String rootPackage = ""; weka.gui.HierarchyPropertyParser hpp = null; // Is this a wrapper toolbar? if (tempBeanCompName.compareTo("null") != 0) { tempBean = null; toolBarType = WEKAWRAPPER_TOOLBAR; rootPackage = (String)tempBarSpecs.elementAt(2); hpp = (weka.gui.HierarchyPropertyParser)tempBarSpecs.elementAt(3); try { Beans.instantiate(null, tempBeanCompName); } catch (Exception ex) { // ignore System.err.println("Failed to instantiate: "+tempBeanCompName); break; } } else { toolBarType = STANDARD_TOOLBAR; } // a toolbar to hold buttons---one for each algorithm JToolBar tempToolBar = new JToolBar(); int z = 2; if (toolBarType == WEKAWRAPPER_TOOLBAR) { if (!hpp.goTo(rootPackage)) { System.err.println("**** Failed to locate root package in tree "); System.exit(1); } String [] primaryPackages = hpp.childrenValues(); for (int kk = 0; kk < primaryPackages.length; kk++) { hpp.goToChild(primaryPackages[kk]); // check to see if this is a leaf - if so then there are no // sub packages if (hpp.isLeafReached()) { // add this bean directly to the tempToolBar String algName = hpp.fullValue(); tempBean = instantiateToolBarBean(true, tempBeanCompName, algName); if (tempBean != null) { tempToolBar.add(tempBean); } hpp.goToParent(); } else { // make a titledborder JPanel to hold all the schemes in this // package JPanel holderPanel = new JPanel(); holderPanel.setBorder(javax.swing.BorderFactory. createTitledBorder(primaryPackages[kk])); processPackage(holderPanel, tempBeanCompName, hpp); tempToolBar.add(holderPanel); } } } else { for (int j = z; j < tempBarSpecs.size(); j++) { tempBean = null; tempBeanCompName = (String)tempBarSpecs.elementAt(j); tempBean = instantiateToolBarBean((toolBarType == WEKAWRAPPER_TOOLBAR), tempBeanCompName, ""); if (tempBean != null) { tempToolBar.add(tempBean); } } } // ok, now create tabbed pane to hold this toolbar JScrollPane tempJScrollPane = new JScrollPane(tempToolBar, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); Dimension d = tempToolBar.getPreferredSize(); tempJScrollPane.setMinimumSize(new Dimension((int)d.getWidth(), (int)(d.getHeight()+15))); tempJScrollPane.setPreferredSize(new Dimension((int)d.getWidth(), (int)(d.getHeight()+15))); m_toolBars.addTab(tempBarName, null, tempJScrollPane, tempBarName); } toolBarPanel.add(m_toolBars, BorderLayout.CENTER); // add(m_toolBars, BorderLayout.NORTH); add(toolBarPanel, BorderLayout.NORTH); } private void processPackage(JPanel holderPanel, String tempBeanCompName, weka.gui.HierarchyPropertyParser hpp) { if (hpp.isLeafReached()) { // instantiate a bean and add it to the holderPanel // System.err.println("Would add "+hpp.fullValue()); String algName = hpp.fullValue(); JPanel tempBean = instantiateToolBarBean(true, tempBeanCompName, algName); if (tempBean != null) { holderPanel.add(tempBean); } hpp.goToParent(); return; } String [] children = hpp.childrenValues(); for (int i = 0; i < children.length; i++) { hpp.goToChild(children[i]); processPackage(holderPanel, tempBeanCompName, hpp); } hpp.goToParent(); } /** * Instantiates a bean for display in the toolbars * * @param wekawrapper true if the bean to be instantiated is a wekawrapper * @param tempBeanCompName the name of the bean to instantiate * @param algName holds the name of a weka algorithm to configure the * bean with if it is a wekawrapper bean * @return a JPanel holding the instantiated (and configured bean) */ private JPanel instantiateToolBarBean(boolean wekawrapper, String tempBeanCompName, String algName) { Object tempBean; if (wekawrapper) { try { tempBean = Beans.instantiate(null, tempBeanCompName); } catch (Exception ex) { System.err.println("Failed to instantiate :"+tempBeanCompName +"KnowledgeFlow.instantiateToolBarBean()"); return null; } if (tempBean instanceof WekaWrapper) { // algName = (String)tempBarSpecs.elementAt(j); Class c = null; try { c = Class.forName(algName); } catch (Exception ex) { System.err.println("Can't find class called: "+algName); return null; } try { Object o = c.newInstance(); ((WekaWrapper)tempBean).setWrappedAlgorithm(o); } catch (Exception ex) { System.err.println("Failed to configure "+tempBeanCompName +" with "+algName); return null; } } } else { try { tempBean = Beans.instantiate(null, tempBeanCompName); } catch (Exception ex) { System.err.println("Failed to instantiate :"+tempBeanCompName +"KnowledgeFlow.setUpToolBars()"); return null; } } // --------------------------------------- JToggleButton tempButton; JPanel tempP = new JPanel(); JLabel tempL = new JLabel(); tempL.setFont(new Font("Monospaced", Font.PLAIN, 10)); String labelName = (wekawrapper == true) ? algName : tempBeanCompName; tempL.setText(" "+labelName. substring(labelName.lastIndexOf('.')+1, labelName.length())+" "); tempL.setHorizontalAlignment(JLabel.CENTER); tempP.setLayout(new BorderLayout()); if (tempBean instanceof Visible) { BeanVisual bv = ((Visible)tempBean).getVisual(); tempButton = new JToggleButton(bv.getStaticIcon()); } else { tempButton = new JToggleButton(); } tempP.add(tempButton, BorderLayout.CENTER); tempP.add(tempL, BorderLayout.SOUTH); // holderPanel.add(tempP); // tempToolBar.add(tempP); m_toolBarGroup.add(tempButton); // add an action listener for the button here final String tempName = tempBeanCompName; final Object tempBN = tempBean; // final JToggleButton tempButton2 = tempButton; tempButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { m_toolBarBean = null; m_toolBarBean = Beans.instantiate(null, tempName); if (m_toolBarBean instanceof WekaWrapper) { Object wrappedAlg = ((WekaWrapper)tempBN).getWrappedAlgorithm(); ((WekaWrapper)m_toolBarBean).setWrappedAlgorithm(wrappedAlg.getClass().newInstance()); // tempButton2.setSelected(false); } setCursor(Cursor. getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); m_mode = ADDING; } catch (Exception ex) { System.err. println("Problem adding bean to data flow layout"); } } }); //return tempBean; return tempP; } /** * Popup a context sensitive menu for the bean component * * @param pt holds the panel coordinates for the component * @param bi the bean component over which the user right clicked the mouse * @param x the x coordinate at which to popup the menu * @param y the y coordinate at which to popup the menu */ private void doPopup(Point pt, final BeanInstance bi, int x, int y) { final JComponent bc = (JComponent)bi.getBean(); final int xx = x; final int yy = y; int menuItemCount = 0; JPopupMenu beanContextMenu = new JPopupMenu(); beanContextMenu.insert(new JLabel("Edit", SwingConstants.CENTER), menuItemCount); menuItemCount++; JMenuItem deleteItem = new JMenuItem("Delete"); deleteItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { BeanConnection.removeConnections(bi); bi.removeBean(m_beanLayout); revalidate(); } }); beanContextMenu.add(deleteItem); menuItemCount++; // first determine if there is a customizer for this bean try { BeanInfo compInfo = Introspector.getBeanInfo(bc.getClass()); if (compInfo == null) { System.err.println("Error"); } else { // System.err.println("Got bean info"); final Class custClass = compInfo.getBeanDescriptor().getCustomizerClass(); if (custClass != null) { // System.err.println("Got customizer class"); // popupCustomizer(custClass, bc); JMenuItem custItem = new JMenuItem("Configure..."); custItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { popupCustomizer(custClass, bc); } }); beanContextMenu.add(custItem); menuItemCount++; } else { System.err.println("No customizer class"); } EventSetDescriptor [] esds = compInfo.getEventSetDescriptors(); if (esds != null && esds.length > 0) { beanContextMenu.insert(new JLabel("Connections", SwingConstants.CENTER), menuItemCount); menuItemCount++; } for (int i = 0; i < esds.length; i++) { // System.err.println(esds[i].getName()); // add each event name to the menu JMenuItem evntItem = new JMenuItem(esds[i].getName()); final EventSetDescriptor esd = esds[i]; // Check EventConstraints (if any) here boolean ok = true; if (bc instanceof EventConstraints) { ok = ((EventConstraints) bc).eventGeneratable(esd.getName()); } if (ok) { evntItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { connectComponents(esd, bi, xx, yy); } }); } else { evntItem.setEnabled(false); } beanContextMenu.add(evntItem); menuItemCount++; } } } catch (IntrospectionException ie) { ie.printStackTrace(); } // System.err.println("Just before look for other options"); // now look for other options for this bean if (bc instanceof UserRequestAcceptor) { Enumeration req = ((UserRequestAcceptor)bc).enumerateRequests(); if (req.hasMoreElements()) { beanContextMenu.insert(new JLabel("Actions", SwingConstants.CENTER), menuItemCount); menuItemCount++; } while (req.hasMoreElements()) { final String tempS = (String)req.nextElement(); JMenuItem custItem = new JMenuItem(tempS); custItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((UserRequestAcceptor)bc).performRequest(tempS); } }); beanContextMenu.add(custItem); menuItemCount++; } } // System.err.println("Just before showing menu"); // popup the menu if (menuItemCount > 0) { beanContextMenu.show(m_beanLayout, x, y); } } /** * Popup the customizer for this bean * * @param custClass the class of the customizer * @param bc the bean to be customized */ private void popupCustomizer(Class custClass, JComponent bc) { try { // instantiate Object customizer = custClass.newInstance(); ((Customizer)customizer).setObject(bc); final javax.swing.JFrame jf = new javax.swing.JFrame(); jf.getContentPane().setLayout(new BorderLayout()); jf.getContentPane().add((JComponent)customizer, BorderLayout.CENTER); jf.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent e) { jf.dispose(); } }); jf.pack(); jf.setVisible(true); } catch (Exception ex) { ex.printStackTrace(); } } /** * Popup a menu giving choices for connections to delete (if any) * * @param closestConnections a vector containing 0 or more BeanConnections * @param x the x coordinate at which to popup the menu * @param y the y coordinate at which to popup the menu */ private void deleteConnectionPopup(Vector closestConnections, int x, int y) { if (closestConnections.size() > 0) { int menuItemCount = 0; JPopupMenu deleteConnectionMenu = new JPopupMenu(); deleteConnectionMenu.insert(new JLabel("Delete Connection", SwingConstants.CENTER), menuItemCount); menuItemCount++; for (int i = 0; i < closestConnections.size(); i++) { final BeanConnection bc = (BeanConnection)closestConnections.elementAt(i); String connName = bc.getSourceEventSetDescriptor().getName(); JMenuItem deleteItem = new JMenuItem(connName); deleteItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { bc.remove(); m_beanLayout.revalidate(); m_beanLayout.repaint(); } }); deleteConnectionMenu.add(deleteItem); menuItemCount++; } deleteConnectionMenu.show(m_beanLayout, x, y); } } /** * Initiates the connection process for two beans * * @param esd the EventSetDescriptor for the source bean * @param bi the source bean * @param x the x coordinate to start connecting from * @param y the y coordinate to start connecting from */ private void connectComponents(EventSetDescriptor esd, BeanInstance bi, int x, int y) { // record the event set descriptior for this event m_sourceEventSetDescriptor = esd; Class listenerClass = esd.getListenerType(); // class of the listener JComponent source = (JComponent)bi.getBean(); // now determine which (if any) of the other beans implement this // listener int targetCount = 0; Vector beanInstances = BeanInstance.getBeanInstances(); for (int i = 0; i < beanInstances.size(); i++) { JComponent bean = (JComponent)((BeanInstance)beanInstances.elementAt(i)).getBean(); boolean connectable = false; if (listenerClass.isInstance(bean) && bean != source) { if (!(bean instanceof BeanCommon)) { connectable = true; // assume this bean is happy to receive a connection } else { // give this bean a chance to veto any proposed connection via // the listener interface if (((BeanCommon)bean). connectionAllowed(esd.getName())) { connectable = true; } } if (connectable) { if (bean instanceof Visible) { targetCount++; ((Visible)bean).getVisual().setDisplayConnectors(true); } } } } // have some possible beans to connect to? if (targetCount > 0) { // System.err.println("target count "+targetCount); if (source instanceof Visible) { ((Visible)source).getVisual().setDisplayConnectors(true); } m_editElement = bi; Point closest = ((Visible)source).getVisual(). getClosestConnectorPoint(new Point(x, y)); m_startX = (int)closest.getX(); m_startY = (int)closest.getY(); m_oldX = m_startX; m_oldY = m_startY; Graphics2D gx = (Graphics2D)m_beanLayout.getGraphics(); gx.setXORMode(java.awt.Color.white); gx.drawLine(m_startX, m_startY, m_startX, m_startY); gx.dispose(); m_mode = CONNECTING; } } private void addComponent(int x, int y) { BeanInstance bi = new BeanInstance(m_beanLayout, m_toolBarBean, x, y); // addBean((JComponent)bi.getBean()); if (m_toolBarBean instanceof Visible) { ((Visible)m_toolBarBean).getVisual().addPropertyChangeListener(this); } if (m_toolBarBean instanceof BeanCommon) { ((BeanCommon)m_toolBarBean).setLog(m_logPanel); } m_toolBarBean = null; setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); m_beanLayout.repaint(); m_pointerB.setSelected(true); m_mode = NONE; } /** * Accept property change events * * @param e a <code>PropertyChangeEvent</code> value */ public void propertyChange(PropertyChangeEvent e) { revalidate(); m_beanLayout.repaint(); } /** * Load a pre-saved layout */ private void loadLayout() { m_loadB.setEnabled(false); m_saveB.setEnabled(false); int returnVal = m_FileChooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { try { File oFile = m_FileChooser.getSelectedFile(); InputStream is = new FileInputStream(oFile); ObjectInputStream ois = new ObjectInputStream(is); Vector beans = (Vector) ois.readObject(); Vector connections = (Vector) ois.readObject(); ois.close(); java.awt.Color bckC = getBackground(); // register this panel as a property change listener with each // bean for (int i = 0; i < beans.size(); i++) { BeanInstance tempB = (BeanInstance)beans.elementAt(i); if (tempB.getBean() instanceof Visible) { ((Visible)(tempB.getBean())).getVisual(). addPropertyChangeListener(this); // A workaround to account for JPanel's with their default // background colour not being serializable in Apple's JRE ((Visible)(tempB.getBean())).getVisual(). setBackground(bckC); ((JComponent)(tempB.getBean())).setBackground(bckC); } if (tempB.getBean() instanceof BeanCommon) { ((BeanCommon)(tempB.getBean())).setLog(m_logPanel); } } BeanInstance.setBeanInstances(beans, m_beanLayout); BeanConnection.setConnections(connections); m_beanLayout.revalidate(); m_beanLayout.repaint(); } catch (Exception ex) { ex.printStackTrace(); } } m_loadB.setEnabled(true); m_saveB.setEnabled(true); } /** * Serialize the layout to a file */ private void saveLayout() { m_loadB.setEnabled(false); m_saveB.setEnabled(false); int returnVal = m_FileChooser.showSaveDialog(this); java.awt.Color bckC = getBackground(); if (returnVal == JFileChooser.APPROVE_OPTION) { // temporarily remove this panel as a property changle listener from // each bean Vector beans = BeanInstance.getBeanInstances(); for (int i = 0; i < beans.size(); i++) { BeanInstance tempB = (BeanInstance)beans.elementAt(i); if (tempB.getBean() instanceof Visible) { ((Visible)(tempB.getBean())).getVisual(). removePropertyChangeListener(this); // A workaround to account for JPanel's with their default // background colour not being serializable in Apple's JRE. // JComponents are rendered with a funky stripy background // under OS X using java.awt.TexturePaint - unfortunately // TexturePaint doesn't implement Serializable. ((Visible)(tempB.getBean())).getVisual(). setBackground(java.awt.Color.white); ((JComponent)(tempB.getBean())).setBackground(java.awt.Color.white); } } // now serialize components vector and connections vector try { File sFile = m_FileChooser.getSelectedFile(); OutputStream os = new FileOutputStream(sFile); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(beans); oos.writeObject(BeanConnection.getConnections()); oos.flush(); oos.close(); } catch (Exception ex) { ex.printStackTrace(); } finally { // restore this panel as a property change listener in the beans for (int i = 0; i < beans.size(); i++) { BeanInstance tempB = (BeanInstance)beans.elementAt(i); if (tempB.getBean() instanceof Visible) { ((Visible)(tempB.getBean())).getVisual(). addPropertyChangeListener(this); // Restore the default background colour ((Visible)(tempB.getBean())).getVisual(). setBackground(bckC); ((JComponent)(tempB.getBean())).setBackground(bckC); } } } } m_saveB.setEnabled(true); m_loadB.setEnabled(true); } /** * Main method. * * @param args a <code>String[]</code> value */ public static void main(String [] args) { try { final javax.swing.JFrame jf = new javax.swing.JFrame(); jf.getContentPane().setLayout(new java.awt.BorderLayout()); final KnowledgeFlow tm = new KnowledgeFlow(); jf.getContentPane().add(tm, java.awt.BorderLayout.CENTER); jf.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent e) { jf.dispose(); System.exit(0); } }); jf.setSize(800,600); jf.setVisible(true); } catch (Exception ex) { ex.printStackTrace(); System.err.println(ex.getMessage()); } } }