source: src/main/java/weka/gui/beans/MetaBean.java @ 24

Last change on this file since 24 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 19.6 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    MetaBean.java
19 *    Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.beans;
24
25import weka.gui.Logger;
26
27import java.awt.BorderLayout;
28import java.awt.Dimension;
29import java.awt.Point;
30import java.awt.event.MouseAdapter;
31import java.awt.event.MouseEvent;
32import java.awt.image.BufferedImage;
33import java.beans.EventSetDescriptor;
34import java.beans.IntrospectionException;
35import java.beans.Introspector;
36import java.beans.PropertyChangeListener;
37import java.io.Serializable;
38import java.util.Enumeration;
39import java.util.Vector;
40
41import javax.swing.ImageIcon;
42import javax.swing.JComponent;
43import javax.swing.JLabel;
44import javax.swing.JPanel;
45import javax.swing.JWindow;
46
47/**
48 * A meta bean that encapsulates several other regular beans, useful for
49 * grouping large KnowledgeFlows.
50 *
51 *
52 * @author Mark Hall (mhall at cs dot waikato dot ac dot nz)
53 * @version $Revision: 6014 $
54 */
55public class MetaBean
56  extends JPanel
57  implements BeanCommon, Visible, EventConstraints,
58             Serializable, UserRequestAcceptor {
59
60  /** for serialization */
61  private static final long serialVersionUID = -6582768902038027077L;
62
63  protected BeanVisual m_visual = 
64    new BeanVisual("Group",
65                   BeanVisual.ICON_PATH+"DiamondPlain.gif",
66                   BeanVisual.ICON_PATH+"DiamondPlain.gif");
67
68  private transient Logger m_log = null;
69  private transient JWindow m_previewWindow = null;
70  private transient javax.swing.Timer m_previewTimer = null;
71
72  protected Vector m_subFlow = new Vector();
73  protected Vector m_inputs = new Vector();
74  protected Vector m_outputs = new Vector();
75
76  // the internal connections for the grouping
77  protected Vector m_associatedConnections = new Vector();
78 
79  // Holds a preview image of the encapsulated sub-flow
80  protected ImageIcon m_subFlowPreview = null;
81
82  public MetaBean() {
83    setLayout(new BorderLayout());
84    add(m_visual, BorderLayout.CENTER);
85  }
86
87  /**
88   * Set a custom (descriptive) name for this bean
89   *
90   * @param name the name to use
91   */
92  public void setCustomName(String name) {
93    m_visual.setText(name);
94  }
95
96  /**
97   * Get the custom (descriptive) name for this bean (if one has been set)
98   *
99   * @return the custom name (or the default name)
100   */
101  public String getCustomName() {
102    return m_visual.getText();
103  }
104
105  public void setAssociatedConnections(Vector ac) {
106    m_associatedConnections = ac;
107  }
108
109  public Vector getAssociatedConnections() {
110    return m_associatedConnections;
111  }
112
113  public void setSubFlow(Vector sub) {
114    m_subFlow = sub;
115  }
116
117  public Vector getSubFlow() {
118    return m_subFlow;
119  }
120
121  public void setInputs(Vector inputs) {
122    m_inputs = inputs;
123  }
124
125  public Vector getInputs() {
126    return m_inputs;
127  }
128
129  public void setOutputs(Vector outputs) {
130    m_outputs = outputs;
131  }
132
133  public Vector getOutputs() {
134    return m_outputs;
135  }
136
137  private Vector getBeans(Vector beans, int type) {
138    Vector comps = new Vector();
139    for (int i = 0; i < beans.size(); i++) {
140      BeanInstance temp = (BeanInstance)beans.elementAt(i);
141      // need to check for sub MetaBean!
142      if (temp.getBean() instanceof MetaBean) {
143        switch (type) {
144        case 0 : 
145          comps.addAll(((MetaBean)temp.getBean()).getBeansInSubFlow());
146          break;
147        case 1 : 
148          comps.addAll(((MetaBean)temp.getBean()).getBeansInInputs());
149          break;
150        case 2:
151          comps.addAll(((MetaBean)temp.getBean()).getBeansInOutputs());
152          break;
153        }
154      } else {
155        comps.add(temp);
156      }
157    }
158    return comps;
159  }
160 
161  private boolean beanSetContains(Vector set, BeanInstance toCheck) {
162    boolean ok = false;
163   
164    for (int i = 0; i < set.size(); i++) {
165      BeanInstance temp = (BeanInstance)set.elementAt(i);
166      if (toCheck == temp) {
167        ok = true;
168        break;
169      }
170    }
171    return ok;
172  }
173 
174  public boolean subFlowContains(BeanInstance toCheck) {
175    return beanSetContains(m_subFlow, toCheck);
176  }
177 
178  public boolean inputsContains(BeanInstance toCheck) {
179    return beanSetContains(m_inputs, toCheck);
180  }
181 
182  public boolean outputsContains(BeanInstance toCheck) {
183    return beanSetContains(m_outputs, toCheck);
184  }
185
186  /**
187   * Return all the beans in the sub flow
188   *
189   * @return a Vector of all the beans in the sub flow
190   */
191  public Vector getBeansInSubFlow() {
192    return getBeans(m_subFlow, 0);
193  }
194
195  /**
196   * Return all the beans in the inputs
197   *
198   * @return a Vector of all the beans in the inputs
199   */
200  public Vector getBeansInInputs() {
201    return getBeans(m_inputs, 1);
202  }
203
204  /**
205   * Return all the beans in the outputs
206   *
207   * @return a Vector of all the beans in the outputs
208   */
209  public Vector getBeansInOutputs() {
210    return getBeans(m_outputs, 2);
211  }
212
213  private Vector getBeanInfos(Vector beans, int type) {
214    Vector infos = new Vector();
215    for (int i = 0; i < beans.size(); i++) {
216      BeanInstance temp = (BeanInstance)beans.elementAt(i);
217      if (temp.getBean() instanceof MetaBean) {
218        switch (type) {
219        case 0: 
220          infos.addAll(((MetaBean)temp.getBean()).getBeanInfoSubFlow());
221          break;
222        case 1: 
223          infos.addAll(((MetaBean)temp.getBean()).getBeanInfoInputs());
224          break;
225        case 2:
226          infos.addAll(((MetaBean)temp.getBean()).getBeanInfoOutputs());
227        }
228      } else {
229        try {
230          infos.add(Introspector.getBeanInfo(temp.getBean().getClass()));
231        } catch (IntrospectionException ex) {
232          ex.printStackTrace();
233        }
234      }
235    }
236    return infos;
237  }
238
239  public Vector getBeanInfoSubFlow() {
240    return getBeanInfos(m_subFlow, 0);
241  }
242
243  public Vector getBeanInfoInputs() {
244    return getBeanInfos(m_inputs, 1);
245  }
246
247  public Vector getBeanInfoOutputs() {
248    return getBeanInfos(m_outputs, 2);
249  }
250
251  // stores the original position of the beans
252  // when this group is created. Used
253  // to restore their locations if the group is ungrouped.
254  private Vector m_originalCoords;
255
256  /**
257   * returns the vector containing the original coordinates (instances of class
258   * Point) for the inputs
259   * @return the containing the coord Points of the original inputs
260   */
261  public Vector getOriginalCoords() {
262    return m_originalCoords;
263  }
264 
265  /**
266   * sets the vector containing the original coordinates (instances of class
267   * Point) for the inputs
268   * @param value the vector containing the points of the coords of the original inputs
269   */
270  public void setOriginalCoords(Vector value) {
271    m_originalCoords = value;
272  }
273
274  /**
275   * Move coords of all inputs and outputs of this meta bean
276   * to the coords of the supplied BeanInstance. Typically
277   * the supplied BeanInstance is the BeanInstance that encapsulates
278   * this meta bean; the result in this case is that all inputs
279   * and outputs are shifted so that their coords coincide with
280   * the meta bean and all connections to them appear (visually) to
281   * go to/from the meta bean.
282   *
283   * @param toShiftTo the BeanInstance whos coordinates will
284   * be used.
285   * @param save true if coordinates are to be saved.
286   */
287  public void shiftBeans(BeanInstance toShiftTo, 
288                         boolean save) {
289    if (save) {
290      m_originalCoords = new Vector();
291    }
292    int targetX = toShiftTo.getX();
293    int targetY = toShiftTo.getY();
294
295    for (int i = 0; i < m_subFlow.size(); i++) {
296      BeanInstance temp = (BeanInstance)m_subFlow.elementAt(i);
297      if (save) {
298        Point p = new Point(temp.getX(), temp.getY());
299        m_originalCoords.add(p);
300      }
301      temp.setX(targetX); temp.setY(targetY);
302    }
303  }
304
305  public void restoreBeans() {
306    for (int i = 0; i < m_subFlow.size(); i++) {
307      BeanInstance temp = (BeanInstance)m_subFlow.elementAt(i);
308      Point p = (Point)m_originalCoords.elementAt(i);
309      JComponent c = (JComponent)temp.getBean();
310      Dimension d = c.getPreferredSize();
311      int dx = (int)(d.getWidth() / 2);
312      int dy = (int)(d.getHeight() / 2);
313      temp.setX((int)p.getX()+dx);
314      temp.setY((int)p.getY()+dy);
315    }
316  }
317
318  /**
319   * Returns true, if at the current time, the event described by the
320   * supplied event descriptor could be generated.
321   *
322   * @param esd an <code>EventSetDescriptor</code> value
323   * @return a <code>boolean</code> value
324   */
325  public boolean eventGeneratable(EventSetDescriptor esd) {
326    String eventName = esd.getName();
327    return eventGeneratable(eventName);
328  }
329
330  /**
331   * Returns true, if at the current time, the named event could
332   * be generated. Assumes that the supplied event name is
333   * an event that could be generated by this bean
334   *
335   * @param eventName the name of the event in question
336   * @return true if the named event could be generated at this point in
337   * time
338   */
339  public boolean eventGeneratable(String eventName) {
340    for (int i = 0; i < m_subFlow.size(); i++) {
341      BeanInstance output = (BeanInstance)m_subFlow.elementAt(i);
342      if (output.getBean() instanceof EventConstraints) {
343        if (((EventConstraints)output.getBean()).eventGeneratable(eventName)) {
344          return true;
345        }
346      }
347    }
348    return false;
349  }
350
351  /**
352   * Returns true if, at this time,
353   * the object will accept a connection with respect to the
354   * supplied EventSetDescriptor
355   *
356   * @param esd the EventSetDescriptor
357   * @return true if the object will accept a connection
358   */
359  public boolean connectionAllowed(EventSetDescriptor esd) {
360    Vector targets = getSuitableTargets(esd);
361    for (int i = 0; i < targets.size(); i++) {
362      BeanInstance input = (BeanInstance)targets.elementAt(i);
363      if (input.getBean() instanceof BeanCommon) {
364        //if (((BeanCommon)input.getBean()).connectionAllowed(esd.getName())) {
365        if (((BeanCommon)input.getBean()).connectionAllowed(esd)) {
366          return true;
367        }
368      } else {
369        return true;
370      }
371    }
372    return false;
373  }
374
375  public boolean connectionAllowed(String eventName) {
376    return false;
377  }
378
379  /**
380   * Notify this object that it has been registered as a listener with
381   * a source with respect to the named event. This is just a dummy
382   * method in this class to satisfy the interface. Specific code
383   * in BeanConnection takes care of this method for MetaBeans
384   *
385   * @param eventName the event
386   * @param source the source with which this object has been registered as
387   * a listener
388   */
389  public synchronized void connectionNotification(String eventName,
390                                                  Object source) {
391  }
392 
393  /**
394   * Notify this object that it has been deregistered as a listener with
395   * a source with respect to the supplied event name. This is just a dummy
396   * method in this class to satisfy the interface. Specific code
397   * in BeanConnection takes care of this method for MetaBeans
398   *
399   * @param eventName the event
400   * @param source the source with which this object has been registered as
401   * a listener
402   */
403  public synchronized void disconnectionNotification(String eventName,
404                                                     Object source) {
405
406  }
407
408  /**
409   * Stop all encapsulated beans
410   */
411  public void stop() {
412    for (int i = 0; i < m_inputs.size(); i++) {
413      Object temp = m_inputs.elementAt(i);
414      if (temp instanceof BeanCommon) {
415        ((BeanCommon)temp).stop();
416      }
417    }
418  }
419 
420  /**
421   * Returns true if. at this time, the bean is busy with some
422   * (i.e. perhaps a worker thread is performing some calculation).
423   *
424   * @return true if the bean is busy.
425   */
426  public boolean isBusy() {
427    boolean result = false;
428    for (int i = 0; i < m_subFlow.size(); i++) {
429      Object temp = m_subFlow.elementAt(i);
430      if (temp instanceof BeanCommon) {
431        if (((BeanCommon)temp).isBusy()) {
432          result = true;
433          break;
434        }
435      }
436    }
437    return result;
438  }
439
440  /**
441   * Sets the visual appearance of this wrapper bean
442   *
443   * @param newVisual a <code>BeanVisual</code> value
444   */
445  public void setVisual(BeanVisual newVisual) {
446    m_visual = newVisual;
447  }
448
449  /**
450   * Gets the visual appearance of this wrapper bean
451   */
452  public BeanVisual getVisual() {
453    return m_visual;
454  }
455
456  /**
457   * Use the default visual appearance for this bean
458   */
459  public void useDefaultVisual() {
460    m_visual.loadIcons(BeanVisual.ICON_PATH+"DiamondPlain.gif",
461                       BeanVisual.ICON_PATH+"DiamondPlain.gif");
462  }
463
464  /**
465   * Return an enumeration of requests that can be made by the user
466   *
467   * @return an <code>Enumeration</code> value
468   */
469  public Enumeration enumerateRequests() {
470    Vector newVector = new Vector();
471    if (m_subFlowPreview != null) {
472      String text = "Show preview";
473      if (m_previewWindow != null) {
474        text = "$"+text;
475      }
476      newVector.addElement(text);
477    }
478    for (int i = 0; i < m_subFlow.size(); i++) {
479      BeanInstance temp = (BeanInstance)m_subFlow.elementAt(i);
480      if (temp.getBean() instanceof UserRequestAcceptor) {
481        String prefix = "";
482        if ((temp.getBean() instanceof BeanCommon)) {
483          prefix = ((BeanCommon)temp.getBean()).getCustomName();
484        } else {
485          prefix = temp.getBean().getClass().getName();
486          prefix = prefix.substring(prefix.lastIndexOf('.')+1, prefix.length());
487        }
488        prefix = ""+(i+1)+": ("+prefix+")";
489        Enumeration en = ((UserRequestAcceptor)temp.getBean()).enumerateRequests();
490        while (en.hasMoreElements()) {
491          String req = (String)en.nextElement();
492          if (req.charAt(0) == '$') {
493            prefix = '$'+prefix;
494            req = req.substring(1, req.length());
495          }
496         
497          if (req.charAt(0) == '?') {
498            prefix = '?' + prefix;
499            req = req.substring(1, req.length());
500          }
501          newVector.add(prefix+" "+req);
502        }         
503      } else if (temp.getBean() instanceof Startable) {
504        String prefix = "";
505        if ((temp.getBean() instanceof BeanCommon)) {
506          prefix = ((BeanCommon)temp.getBean()).getCustomName();
507        } else {
508          prefix = temp.getBean().getClass().getName();
509          prefix = prefix.substring(prefix.lastIndexOf('.')+1, prefix.length());
510        }
511        prefix = ""+(i+1)+": ("+prefix+")";
512        String startMessage = ((Startable)temp.getBean()).getStartMessage();
513        if (startMessage.charAt(0) == '$') {
514          prefix = '$'+prefix;
515          startMessage = startMessage.substring(1, startMessage.length());
516        }
517        newVector.add(prefix + " " + startMessage);
518      }
519    }
520   
521    return newVector.elements();
522  }
523 
524  public void setSubFlowPreview(ImageIcon sfp) {
525    m_subFlowPreview = sfp;
526  }
527 
528  private void showPreview() {
529    if (m_previewWindow == null) {
530     
531      JLabel jl = new JLabel(m_subFlowPreview);
532      //Dimension d = jl.getPreferredSize();
533      jl.setLocation(0,0);
534      m_previewWindow = new JWindow();
535      //popup.getContentPane().setLayout(null);
536      m_previewWindow.getContentPane().add(jl);
537      m_previewWindow.validate();
538      m_previewWindow.setSize(m_subFlowPreview.getIconWidth(), m_subFlowPreview.getIconHeight());
539     
540      m_previewWindow.addMouseListener(new MouseAdapter() {
541          public void mouseClicked(MouseEvent e) {
542            m_previewWindow.dispose();
543            m_previewWindow = null;
544          }
545        });
546     
547      m_previewWindow.setLocation(
548          getParent().getLocationOnScreen().x + getX() + getWidth() / 2 - 
549          m_subFlowPreview.getIconWidth() / 2, 
550          getParent().getLocationOnScreen().y + getY() + getHeight() / 2 - 
551          m_subFlowPreview.getIconHeight() / 2);
552      //popup.pack();
553      m_previewWindow.setVisible(true);
554      m_previewTimer = 
555        new javax.swing.Timer(8000, new java.awt.event.ActionListener() {
556          public void actionPerformed(java.awt.event.ActionEvent e) {
557            if (m_previewWindow != null) {
558              m_previewWindow.dispose();
559              m_previewWindow = null;
560              m_previewTimer = null;
561            }
562          }
563        });
564      m_previewTimer.setRepeats(false);
565      m_previewTimer.start();
566    }
567  }
568
569  /**
570   * Perform a particular request
571   *
572   * @param request the request to perform
573   * @exception IllegalArgumentException if an error occurs
574   */
575  public void performRequest(String request) {
576    if (request.compareTo("Show preview") == 0) {
577      showPreview();
578      return;
579    }
580    // first grab the index if any
581    if (request.indexOf(":") < 0) {
582      return;
583    }
584    String tempI = request.substring(0, request.indexOf(':'));
585    int index = Integer.parseInt(tempI);
586    index--;
587    String req = request.substring(request.indexOf(')')+1, 
588                                   request.length()).trim();
589   
590    Object target = (((BeanInstance)m_subFlow.elementAt(index)).getBean());
591    if (target instanceof Startable && req.equals(((Startable)target).getStartMessage())) {
592      try {
593        ((Startable)target).start();
594      } catch (Exception ex) {
595        if (m_log != null) {
596          String compName = (target instanceof BeanCommon) ? ((BeanCommon)target).getCustomName() : "";
597          m_log.logMessage("Problem starting subcomponent " + compName);
598        }
599      }
600    } else {   
601      ((UserRequestAcceptor)target).performRequest(req);
602    }                                   
603  }
604
605  /**
606   * Set a logger
607   *
608   * @param logger a <code>Logger</code> value
609   */
610  public void setLog(Logger logger) {
611    m_log = logger;
612  }
613
614  public void removePropertyChangeListenersSubFlow(PropertyChangeListener pcl) {
615    for (int i = 0; i < m_subFlow.size(); i++) {
616      BeanInstance temp = (BeanInstance)m_subFlow.elementAt(i);
617      if (temp.getBean() instanceof Visible) {
618        ((Visible)(temp.getBean())).getVisual().
619          removePropertyChangeListener(pcl);
620      }
621      if (temp.getBean() instanceof MetaBean) {
622        ((MetaBean)temp.getBean()).removePropertyChangeListenersSubFlow(pcl);
623      }
624    }
625  }
626
627  public void addPropertyChangeListenersSubFlow(PropertyChangeListener pcl) {
628    for (int i = 0; i < m_subFlow.size(); i++) {
629      BeanInstance temp = (BeanInstance)m_subFlow.elementAt(i);
630      if (temp.getBean() instanceof Visible) {
631        ((Visible)(temp.getBean())).getVisual().
632          addPropertyChangeListener(pcl);
633      }
634      if (temp.getBean() instanceof MetaBean) {
635        ((MetaBean)temp.getBean()).addPropertyChangeListenersSubFlow(pcl);
636      }
637    }
638  }
639
640  /**
641   * Checks to see if any of the inputs to this group implements
642   * the supplied listener class
643   *
644   * @param listenerClass the listener to check for
645   */
646  public boolean canAcceptConnection(Class listenerClass) {
647    for (int i = 0; i < m_inputs.size(); i++) {
648      BeanInstance input = (BeanInstance)m_inputs.elementAt(i);
649      if (listenerClass.isInstance(input.getBean())) {
650        return true;
651      }
652    }
653    return false;
654  }
655
656  /**
657   * Return a list of input beans capable of receiving the
658   * supplied event
659   *
660   * @param esd the event in question
661   * @return a vector of beans capable of handling the event
662   */
663  public Vector getSuitableTargets(EventSetDescriptor esd) {
664    Class listenerClass = esd.getListenerType(); // class of the listener
665    Vector targets = new Vector();
666    for (int i = 0; i < m_inputs.size(); i++) {
667      BeanInstance input = (BeanInstance)m_inputs.elementAt(i);
668      if (listenerClass.isInstance(input.getBean())) {
669        targets.add(input);
670      }
671    }
672    return targets;
673  }
674}
Note: See TracBrowser for help on using the repository browser.