source: src/main/java/weka/gui/beans/Associator.java @ 15

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

Import di weka.

File size: 17.1 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 *    Associator.java
19 *    Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.beans;
24
25import weka.associations.Apriori;
26import weka.core.Instances;
27import weka.core.OptionHandler;
28import weka.core.Utils;
29import weka.gui.Logger;
30
31import java.awt.BorderLayout;
32import java.beans.EventSetDescriptor;
33import java.io.Serializable;
34import java.util.Enumeration;
35import java.util.Hashtable;
36import java.util.Vector;
37
38import javax.swing.JPanel;
39
40/**
41 * Bean that wraps around weka.associations
42 *
43 * @author Mark Hall (mhall at cs dot waikato dot ac dot nz)
44 * @version $Revision: 5247 $
45 * @since 1.0
46 * @see JPanel
47 * @see BeanCommon
48 * @see Visible
49 * @see WekaWrapper
50 * @see Serializable
51 * @see UserRequestAcceptor
52 * @see TrainingSetListener
53 * @see DataSourceListener
54 */
55public class Associator
56  extends JPanel
57  implements BeanCommon, Visible, 
58             WekaWrapper, EventConstraints,
59             Serializable, UserRequestAcceptor,
60             DataSourceListener,
61             TrainingSetListener, ConfigurationProducer {
62
63  /** for serialization */
64  private static final long serialVersionUID = -7843500322130210057L;
65
66  protected BeanVisual m_visual = 
67    new BeanVisual("Associator",
68                   BeanVisual.ICON_PATH+"DefaultAssociator.gif",
69                   BeanVisual.ICON_PATH+"DefaultAssociator_animated.gif");
70
71  private static int IDLE = 0;
72  private static int BUILDING_MODEL = 1;
73
74  private int m_state = IDLE;
75
76  private Thread m_buildThread = null;
77
78  /**
79   * Global info for the wrapped associator (if it exists).
80   */
81  protected String m_globalInfo;
82
83  /**
84   * Objects talking to us
85   */
86  private Hashtable m_listenees = new Hashtable();
87
88  /**
89   * Objects listening for text events
90   */
91  private Vector m_textListeners = new Vector();
92
93  /**
94   * Objects listening for graph events
95   */
96  private Vector m_graphListeners = new Vector();
97
98  private weka.associations.Associator m_Associator = new Apriori();
99
100  private transient Logger m_log = null;
101
102  /**
103   * Global info (if it exists) for the wrapped classifier
104   *
105   * @return the global info
106   */
107  public String globalInfo() {
108    return m_globalInfo;
109  }
110
111  /**
112   * Creates a new <code>Associator</code> instance.
113   */
114  public Associator() {
115    setLayout(new BorderLayout());
116    add(m_visual, BorderLayout.CENTER);
117    setAssociator(m_Associator);
118  }
119
120  /**
121   * Set a custom (descriptive) name for this bean
122   *
123   * @param name the name to use
124   */
125  public void setCustomName(String name) {
126    m_visual.setText(name);
127  }
128
129  /**
130   * Get the custom (descriptive) name for this bean (if one has been set)
131   *
132   * @return the custom name (or the default name)
133   */
134  public String getCustomName() {
135    return m_visual.getText();
136  }
137
138  /**
139   * Set the associator for this wrapper
140   *
141   * @param c a <code>weka.associations.Associator</code> value
142   */
143  public void setAssociator(weka.associations.Associator c) {
144    boolean loadImages = true;
145    if (c.getClass().getName().
146        compareTo(m_Associator.getClass().getName()) == 0) {
147      loadImages = false;
148    } 
149    m_Associator = c;
150    String associatorName = c.getClass().toString();
151    associatorName = associatorName.substring(associatorName.
152                                              lastIndexOf('.')+1, 
153                                              associatorName.length());
154    if (loadImages) {
155      if (!m_visual.loadIcons(BeanVisual.ICON_PATH+associatorName+".gif",
156                       BeanVisual.ICON_PATH+associatorName+"_animated.gif")) {
157        useDefaultVisual();
158      }
159    }
160    m_visual.setText(associatorName);
161
162    // get global info
163    m_globalInfo = KnowledgeFlowApp.getGlobalInfo(m_Associator);
164  }
165 
166  /**
167   * Get the associator currently set for this wrapper
168   *
169   * @return a <code>weka.associations.Associator</code> value
170   */
171  public weka.associations.Associator getAssociator() {
172    return m_Associator;
173  }
174
175  /**
176   * Sets the algorithm (associator) for this bean
177   *
178   * @param algorithm an <code>Object</code> value
179   * @exception IllegalArgumentException if an error occurs
180   */
181  public void setWrappedAlgorithm(Object algorithm) {
182
183    if (!(algorithm instanceof weka.associations.Associator)) { 
184      throw new IllegalArgumentException(algorithm.getClass()+" : incorrect "
185                                         +"type of algorithm (Associator)");
186    }
187    setAssociator((weka.associations.Associator)algorithm);
188  }
189
190  /**
191   * Returns the wrapped associator
192   *
193   * @return an <code>Object</code> value
194   */
195  public Object getWrappedAlgorithm() {
196    return getAssociator();
197  }
198
199  /**
200   * Accept a training set
201   *
202   * @param e a <code>TrainingSetEvent</code> value
203   */
204  public void acceptTrainingSet(TrainingSetEvent e) {
205    // construct and pass on a DataSetEvent
206    Instances trainingSet = e.getTrainingSet();
207    DataSetEvent dse = new DataSetEvent(this, trainingSet);
208    acceptDataSet(dse);
209  }
210
211  public void acceptDataSet(final DataSetEvent e) {
212    if (e.isStructureOnly()) {
213      // no need to build an associator, just absorb and return
214      return;
215    }
216
217
218    if (m_buildThread == null) {
219      try {
220        if (m_state == IDLE) {
221          synchronized (this) {
222            m_state = BUILDING_MODEL;
223          }
224          final Instances trainingData = e.getDataSet();
225//        final String oldText = m_visual.getText();
226          m_buildThread = new Thread() {
227              public void run() {
228                try {
229                  if (trainingData != null) {
230                    m_visual.setAnimated();
231//                  m_visual.setText("Building model...");
232                    if (m_log != null) {
233                      m_log.statusMessage(statusMessagePrefix() 
234                          + "Building model...");
235                    }
236                    buildAssociations(trainingData);
237
238                    if (m_textListeners.size() > 0) {
239                      String modelString = m_Associator.toString();
240                      String titleString = m_Associator.getClass().getName();
241                     
242                      titleString = titleString.
243                        substring(titleString.lastIndexOf('.') + 1,
244                                  titleString.length());
245                      modelString = "=== Associator model ===\n\n" +
246                        "Scheme:   " +titleString+"\n" +
247                        "Relation: "  + trainingData.relationName() + 
248                        "\n\n"
249                        + modelString;
250                      titleString = "Model: " + titleString;
251
252                      TextEvent nt = new TextEvent(Associator.this,
253                                                   modelString,
254                                                   titleString);
255                      notifyTextListeners(nt);
256                    }
257
258                    if (m_Associator instanceof weka.core.Drawable && 
259                        m_graphListeners.size() > 0) {
260                      String grphString = 
261                        ((weka.core.Drawable)m_Associator).graph();
262                      int grphType = ((weka.core.Drawable)m_Associator).graphType();
263                      String grphTitle = m_Associator.getClass().getName();
264                      grphTitle = grphTitle.substring(grphTitle.
265                                                      lastIndexOf('.')+1, 
266                                                      grphTitle.length());
267                      grphTitle = " ("
268                        +e.getDataSet().relationName() + ") "
269                        +grphTitle;
270                     
271                      GraphEvent ge = new GraphEvent(Associator.this, 
272                                                     grphString, 
273                                                     grphTitle,
274                                                     grphType);
275                      notifyGraphListeners(ge);
276                    }
277                  }
278                } catch (Exception ex) {
279                  Associator.this.stop();
280                  if (m_log != null) {
281                    m_log.statusMessage(statusMessagePrefix()
282                        + "ERROR (See log for details)");
283                    m_log.logMessage("[Associator] " + statusMessagePrefix()
284                        + " problem training associator. " + ex.getMessage());
285                  }
286                  ex.printStackTrace();
287                } finally {
288//                m_visual.setText(oldText);
289                  m_visual.setStatic();
290                  m_state = IDLE;
291                  if (isInterrupted()) {
292                    if (m_log != null) {
293                      String titleString = m_Associator.getClass().getName();                 
294                      titleString = titleString.
295                        substring(titleString.lastIndexOf('.') + 1,
296                                  titleString.length());
297                      m_log.logMessage("[Associator] " + statusMessagePrefix() 
298                          + " Build associator interrupted!");
299                      m_log.statusMessage(statusMessagePrefix() + "INTERRUPTED");
300                    }
301                  } else {
302                    if (m_log != null) {
303                      m_log.statusMessage(statusMessagePrefix() + "Finished.");
304                    }
305                  }
306                  block(false);
307                }
308              } 
309            };
310          m_buildThread.setPriority(Thread.MIN_PRIORITY);
311          m_buildThread.start();
312          // make sure the thread is still running before we block
313          //      if (m_buildThread.isAlive()) {
314          block(true);
315            //    }
316          m_buildThread = null;
317          m_state = IDLE;
318        }
319      } catch (Exception ex) {
320        ex.printStackTrace();
321      }
322    }
323  }
324
325
326  private void buildAssociations(Instances data) 
327    throws Exception {
328    m_Associator.buildAssociations(data);
329  }
330
331  /**
332   * Sets the visual appearance of this wrapper bean
333   *
334   * @param newVisual a <code>BeanVisual</code> value
335   */
336  public void setVisual(BeanVisual newVisual) {
337    m_visual = newVisual;
338  }
339
340  /**
341   * Gets the visual appearance of this wrapper bean
342   */
343  public BeanVisual getVisual() {
344    return m_visual;
345  }
346
347  /**
348   * Use the default visual appearance for this bean
349   */
350  public void useDefaultVisual() {
351    m_visual.loadIcons(BeanVisual.ICON_PATH+"DefaultAssociator.gif",
352                       BeanVisual.ICON_PATH+"DefaultAssociator_animated.gif");
353  }
354
355  /**
356   * Add a text listener
357   *
358   * @param cl a <code>TextListener</code> value
359   */
360  public synchronized void addTextListener(TextListener cl) {
361    m_textListeners.addElement(cl);
362  }
363
364  /**
365   * Remove a text listener
366   *
367   * @param cl a <code>TextListener</code> value
368   */
369  public synchronized void removeTextListener(TextListener cl) {
370    m_textListeners.remove(cl);
371  }
372
373  /**
374   * Add a graph listener
375   *
376   * @param cl a <code>GraphListener</code> value
377   */
378  public synchronized void addGraphListener(GraphListener cl) {
379    m_graphListeners.addElement(cl);
380  }
381
382  /**
383   * Remove a graph listener
384   *
385   * @param cl a <code>GraphListener</code> value
386   */
387  public synchronized void removeGraphListener(GraphListener cl) {
388    m_graphListeners.remove(cl);
389  }
390 
391  /**
392   * We don't have to keep track of configuration listeners (see the
393   * documentation for ConfigurationListener/ConfigurationEvent).
394   *
395   * @param cl a ConfigurationListener.
396   */
397  public synchronized void addConfigurationListener(ConfigurationListener cl) {
398   
399  }
400 
401  /**
402   * We don't have to keep track of configuration listeners (see the
403   * documentation for ConfigurationListener/ConfigurationEvent).
404   *
405   * @param cl a ConfigurationListener.
406   */
407  public synchronized void removeConfigurationListener(ConfigurationListener cl) {
408   
409  }
410
411  /**
412   * Notify all text listeners of a text event
413   *
414   * @param ge a <code>TextEvent</code> value
415   */
416  private void notifyTextListeners(TextEvent ge) {
417    Vector l;
418    synchronized (this) {
419      l = (Vector)m_textListeners.clone();
420    }
421    if (l.size() > 0) {
422      for(int i = 0; i < l.size(); i++) {
423        ((TextListener)l.elementAt(i)).acceptText(ge);
424      }
425    }
426  }
427
428  /**
429   * Notify all graph listeners of a graph event
430   *
431   * @param ge a <code>GraphEvent</code> value
432   */
433  private void notifyGraphListeners(GraphEvent ge) {
434    Vector l;
435    synchronized (this) {
436      l = (Vector)m_graphListeners.clone();
437    }
438    if (l.size() > 0) {
439      for(int i = 0; i < l.size(); i++) {
440        ((GraphListener)l.elementAt(i)).acceptGraph(ge);
441      }
442    }
443  }
444
445  /**
446   * Returns true if, at this time,
447   * the object will accept a connection with respect to the named event
448   *
449   * @param eventName the event
450   * @return true if the object will accept a connection
451   */
452  public boolean connectionAllowed(String eventName) {
453    if (m_listenees.containsKey(eventName)) {
454      return false;
455    }
456    return true;
457  }
458
459  /**
460   * Returns true if, at this time,
461   * the object will accept a connection according to the supplied
462   * EventSetDescriptor
463   *
464   * @param esd the EventSetDescriptor
465   * @return true if the object will accept a connection
466   */
467  public boolean connectionAllowed(EventSetDescriptor esd) {
468    return connectionAllowed(esd.getName());
469  }
470
471 
472  /**
473   * Notify this object that it has been registered as a listener with
474   * a source with respect to the named event
475   *
476   * @param eventName the event
477   * @param source the source with which this object has been registered as
478   * a listener
479   */
480  public synchronized void connectionNotification(String eventName,
481                                                  Object source) {
482
483    if (connectionAllowed(eventName)) {
484      m_listenees.put(eventName, source);
485    }
486  }
487
488  /**
489   * Notify this object that it has been deregistered as a listener with
490   * a source with respect to the supplied event name
491   *
492   * @param eventName the event
493   * @param source the source with which this object has been registered as
494   * a listener
495   */
496  public synchronized void disconnectionNotification(String eventName,
497                                                     Object source) {
498    m_listenees.remove(eventName);
499  }
500
501  /**
502   * Function used to stop code that calls acceptTrainingSet. This is
503   * needed as classifier construction is performed inside a separate
504   * thread of execution.
505   *
506   * @param tf a <code>boolean</code> value
507   */
508  private synchronized void block(boolean tf) {
509
510    if (tf) {
511      try {
512          // only block if thread is still doing something useful!
513        if (m_buildThread.isAlive() && m_state != IDLE) {
514          wait();
515          }
516      } catch (InterruptedException ex) {
517      }
518    } else {
519      notifyAll();
520    }
521  } 
522 
523  /**
524   * Returns true if. at this time, the bean is busy with some
525   * (i.e. perhaps a worker thread is performing some calculation).
526   *
527   * @return true if the bean is busy.
528   */
529  public boolean isBusy() {
530    return (m_buildThread != null);
531  }
532
533  /**
534   * Stop any associator action
535   */
536  public void stop() {
537    // tell all listenees (upstream beans) to stop
538    Enumeration en = m_listenees.keys();
539    while (en.hasMoreElements()) {
540      Object tempO = m_listenees.get(en.nextElement());
541      if (tempO instanceof BeanCommon) {
542        ((BeanCommon)tempO).stop();
543      }
544    }
545
546    // stop the build thread
547    if (m_buildThread != null) {
548      m_buildThread.interrupt();
549      m_buildThread.stop();
550      m_buildThread = null;
551      m_visual.setStatic();
552    }
553  }
554
555  /**
556   * Set a logger
557   *
558   * @param logger a <code>Logger</code> value
559   */
560  public void setLog(Logger logger) {
561    m_log = logger;
562  }
563
564  /**
565   * Return an enumeration of requests that can be made by the user
566   *
567   * @return an <code>Enumeration</code> value
568   */
569  public Enumeration enumerateRequests() {
570    Vector newVector = new Vector(0);
571    if (m_buildThread != null) {
572      newVector.addElement("Stop");
573    }
574    return newVector.elements();
575  }
576
577  /**
578   * Perform a particular request
579   *
580   * @param request the request to perform
581   * @exception IllegalArgumentException if an error occurs
582   */
583  public void performRequest(String request) {
584    if (request.compareTo("Stop") == 0) {
585      stop();
586    } else {
587      throw new IllegalArgumentException(request
588                                         + " not supported (Associator)");
589    }
590  }
591
592  /**
593   * Returns true, if at the current time, the event described by the
594   * supplied event descriptor could be generated.
595   *
596   * @param esd an <code>EventSetDescriptor</code> value
597   * @return a <code>boolean</code> value
598   */
599  public boolean eventGeneratable(EventSetDescriptor esd) {
600    String eventName = esd.getName();
601    return eventGeneratable(eventName);
602  }
603
604  /**
605   * Returns true, if at the current time, the named event could
606   * be generated. Assumes that the supplied event name is
607   * an event that could be generated by this bean
608   *
609   * @param eventName the name of the event in question
610   * @return true if the named event could be generated at this point in
611   * time
612   */
613  public boolean eventGeneratable(String eventName) {
614    if (eventName.compareTo("text") == 0 ||
615        eventName.compareTo("graph") == 0) {
616      if (!m_listenees.containsKey("dataSet") &&
617          !m_listenees.containsKey("trainingSet")) {
618        return false;
619      }
620      Object source = m_listenees.get("trainingSet");
621      if (source != null && source instanceof EventConstraints) {
622        if (!((EventConstraints)source).eventGeneratable("trainingSet")) {
623          return false;
624        }
625      }
626      source = m_listenees.get("dataSet");
627      if (source != null && source instanceof EventConstraints) {
628        if (!((EventConstraints)source).eventGeneratable("dataSet")) {
629          return false;
630        }
631      }
632
633      if (eventName.compareTo("graph") == 0 &&
634          !(m_Associator instanceof weka.core.Drawable)) {
635        return false;
636      }
637    }
638    return true;
639  }
640 
641  private String statusMessagePrefix() {
642    return getCustomName() + "$" + hashCode() + "|"
643    + ((m_Associator instanceof OptionHandler && 
644        Utils.joinOptions(((OptionHandler)m_Associator).getOptions()).length() > 0) 
645        ? Utils.joinOptions(((OptionHandler)m_Associator).getOptions()) + "|"
646            : "");
647  }
648}
Note: See TracBrowser for help on using the repository browser.