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.
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.
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.
19
* Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
23
package weka.gui.beans;
25
import weka.associations.Apriori;
26
import weka.core.Instances;
27
import weka.gui.Logger;
29
import java.awt.BorderLayout;
30
import java.beans.EventSetDescriptor;
31
import java.io.Serializable;
32
import java.util.Enumeration;
33
import java.util.Hashtable;
34
import java.util.Vector;
36
import javax.swing.JPanel;
39
* Bean that wraps around weka.associations
41
* @author Mark Hall (mhall at cs dot waikato dot ac dot nz)
42
* @version $Revision: 1.3 $
49
* @see UserRequestAcceptor
50
* @see TrainingSetListener
51
* @see DataSourceListener
53
public class Associator
55
implements BeanCommon, Visible,
56
WekaWrapper, EventConstraints,
57
Serializable, UserRequestAcceptor,
61
/** for serialization */
62
private static final long serialVersionUID = -7843500322130210057L;
64
protected BeanVisual m_visual =
65
new BeanVisual("Associator",
66
BeanVisual.ICON_PATH+"DefaultAssociator.gif",
67
BeanVisual.ICON_PATH+"DefaultAssociator_animated.gif");
69
private static int IDLE = 0;
70
private static int BUILDING_MODEL = 1;
72
private int m_state = IDLE;
74
private Thread m_buildThread = null;
77
* Global info for the wrapped associator (if it exists).
79
protected String m_globalInfo;
82
* Objects talking to us
84
private Hashtable m_listenees = new Hashtable();
87
* Objects listening for text events
89
private Vector m_textListeners = new Vector();
91
private weka.associations.Associator m_Associator = new Apriori();
93
private transient Logger m_log = null;
96
* Global info (if it exists) for the wrapped classifier
98
* @return the global info
100
public String globalInfo() {
105
* Creates a new <code>Associator</code> instance.
107
public Associator() {
108
setLayout(new BorderLayout());
109
add(m_visual, BorderLayout.CENTER);
110
setAssociator(m_Associator);
114
* Set the associator for this wrapper
116
* @param c a <code>weka.associations.Associator</code> value
118
public void setAssociator(weka.associations.Associator c) {
119
boolean loadImages = true;
120
if (c.getClass().getName().
121
compareTo(m_Associator.getClass().getName()) == 0) {
125
String associatorName = c.getClass().toString();
126
associatorName = associatorName.substring(associatorName.
128
associatorName.length());
130
if (!m_visual.loadIcons(BeanVisual.ICON_PATH+associatorName+".gif",
131
BeanVisual.ICON_PATH+associatorName+"_animated.gif")) {
135
m_visual.setText(associatorName);
138
m_globalInfo = KnowledgeFlowApp.getGlobalInfo(m_Associator);
142
* Get the associator currently set for this wrapper
144
* @return a <code>weka.associations.Associator</code> value
146
public weka.associations.Associator getAssociator() {
151
* Sets the algorithm (associator) for this bean
153
* @param algorithm an <code>Object</code> value
154
* @exception IllegalArgumentException if an error occurs
156
public void setWrappedAlgorithm(Object algorithm) {
158
if (!(algorithm instanceof weka.associations.Associator)) {
159
throw new IllegalArgumentException(algorithm.getClass()+" : incorrect "
160
+"type of algorithm (Associator)");
162
setAssociator((weka.associations.Associator)algorithm);
166
* Returns the wrapped associator
168
* @return an <code>Object</code> value
170
public Object getWrappedAlgorithm() {
171
return getAssociator();
175
* Accept a training set
177
* @param e a <code>TrainingSetEvent</code> value
179
public void acceptTrainingSet(TrainingSetEvent e) {
180
// construct and pass on a DataSetEvent
181
Instances trainingSet = e.getTrainingSet();
182
DataSetEvent dse = new DataSetEvent(this, trainingSet);
186
public void acceptDataSet(final DataSetEvent e) {
187
if (e.isStructureOnly()) {
188
// no need to build an associator, just absorb and return
193
if (m_buildThread == null) {
195
if (m_state == IDLE) {
196
synchronized (this) {
197
m_state = BUILDING_MODEL;
199
final Instances trainingData = e.getDataSet();
200
final String oldText = m_visual.getText();
201
m_buildThread = new Thread() {
204
if (trainingData != null) {
205
m_visual.setAnimated();
206
m_visual.setText("Building model...");
208
m_log.statusMessage("Associator : building model...");
210
buildAssociations(trainingData);
212
if (m_textListeners.size() > 0) {
213
String modelString = m_Associator.toString();
214
String titleString = m_Associator.getClass().getName();
216
titleString = titleString.
217
substring(titleString.lastIndexOf('.') + 1,
218
titleString.length());
219
modelString = "=== Associator model ===\n\n" +
220
"Scheme: " +titleString+"\n" +
221
"Relation: " + trainingData.relationName() +
224
titleString = "Model: " + titleString;
226
TextEvent nt = new TextEvent(Associator.this,
229
notifyTextListeners(nt);
232
} catch (Exception ex) {
233
ex.printStackTrace();
235
m_visual.setText(oldText);
236
m_visual.setStatic();
238
if (isInterrupted()) {
240
m_log.logMessage("Build associator interrupted!");
241
m_log.statusMessage("OK");
245
m_log.statusMessage("OK");
251
m_buildThread.setPriority(Thread.MIN_PRIORITY);
252
m_buildThread.start();
253
// make sure the thread is still running before we block
254
// if (m_buildThread.isAlive()) {
257
m_buildThread = null;
260
} catch (Exception ex) {
261
ex.printStackTrace();
267
private void buildAssociations(Instances data)
269
m_Associator.buildAssociations(data);
273
* Sets the visual appearance of this wrapper bean
275
* @param newVisual a <code>BeanVisual</code> value
277
public void setVisual(BeanVisual newVisual) {
278
m_visual = newVisual;
282
* Gets the visual appearance of this wrapper bean
284
public BeanVisual getVisual() {
289
* Use the default visual appearance for this bean
291
public void useDefaultVisual() {
292
m_visual.loadIcons(BeanVisual.ICON_PATH+"DefaultAssociator.gif",
293
BeanVisual.ICON_PATH+"DefaultAssociator_animated.gif");
297
* Add a text listener
299
* @param cl a <code>TextListener</code> value
301
public synchronized void addTextListener(TextListener cl) {
302
m_textListeners.addElement(cl);
306
* Remove a text listener
308
* @param cl a <code>TextListener</code> value
310
public synchronized void removeTextListener(TextListener cl) {
311
m_textListeners.remove(cl);
315
* Notify all text listeners of a text event
317
* @param ge a <code>TextEvent</code> value
319
private void notifyTextListeners(TextEvent ge) {
321
synchronized (this) {
322
l = (Vector)m_textListeners.clone();
325
for(int i = 0; i < l.size(); i++) {
326
((TextListener)l.elementAt(i)).acceptText(ge);
332
* Returns true if, at this time,
333
* the object will accept a connection with respect to the named event
335
* @param eventName the event
336
* @return true if the object will accept a connection
338
public boolean connectionAllowed(String eventName) {
339
if (m_listenees.containsKey(eventName)) {
346
* Returns true if, at this time,
347
* the object will accept a connection according to the supplied
350
* @param esd the EventSetDescriptor
351
* @return true if the object will accept a connection
353
public boolean connectionAllowed(EventSetDescriptor esd) {
354
return connectionAllowed(esd.getName());
359
* Notify this object that it has been registered as a listener with
360
* a source with respect to the named event
362
* @param eventName the event
363
* @param source the source with which this object has been registered as
366
public synchronized void connectionNotification(String eventName,
369
if (connectionAllowed(eventName)) {
370
m_listenees.put(eventName, source);
375
* Notify this object that it has been deregistered as a listener with
376
* a source with respect to the supplied event name
378
* @param eventName the event
379
* @param source the source with which this object has been registered as
382
public synchronized void disconnectionNotification(String eventName,
384
m_listenees.remove(eventName);
388
* Function used to stop code that calls acceptTrainingSet. This is
389
* needed as classifier construction is performed inside a separate
390
* thread of execution.
392
* @param tf a <code>boolean</code> value
394
private synchronized void block(boolean tf) {
398
// only block if thread is still doing something useful!
399
if (m_buildThread.isAlive() && m_state != IDLE) {
402
} catch (InterruptedException ex) {
410
* Stop any associator action
413
// tell all listenees (upstream beans) to stop
414
Enumeration en = m_listenees.keys();
415
while (en.hasMoreElements()) {
416
Object tempO = m_listenees.get(en.nextElement());
417
if (tempO instanceof BeanCommon) {
418
System.err.println("Listener is BeanCommon");
419
((BeanCommon)tempO).stop();
423
// stop the build thread
424
if (m_buildThread != null) {
425
m_buildThread.interrupt();
426
m_buildThread.stop();
427
m_buildThread = null;
428
m_visual.setStatic();
435
* @param logger a <code>Logger</code> value
437
public void setLog(Logger logger) {
442
* Return an enumeration of requests that can be made by the user
444
* @return an <code>Enumeration</code> value
446
public Enumeration enumerateRequests() {
447
Vector newVector = new Vector(0);
448
if (m_buildThread != null) {
449
newVector.addElement("Stop");
451
return newVector.elements();
455
* Perform a particular request
457
* @param request the request to perform
458
* @exception IllegalArgumentException if an error occurs
460
public void performRequest(String request) {
461
if (request.compareTo("Stop") == 0) {
464
throw new IllegalArgumentException(request
465
+ " not supported (Associator)");
470
* Returns true, if at the current time, the event described by the
471
* supplied event descriptor could be generated.
473
* @param esd an <code>EventSetDescriptor</code> value
474
* @return a <code>boolean</code> value
476
public boolean eventGeneratable(EventSetDescriptor esd) {
477
String eventName = esd.getName();
478
return eventGeneratable(eventName);
482
* Returns true, if at the current time, the named event could
483
* be generated. Assumes that the supplied event name is
484
* an event that could be generated by this bean
486
* @param eventName the name of the event in question
487
* @return true if the named event could be generated at this point in
490
public boolean eventGeneratable(String eventName) {
491
if (eventName.compareTo("text") == 0) {
492
if (!m_listenees.containsKey("dataSet") &&
493
!m_listenees.containsKey("trainingSet")) {
496
Object source = m_listenees.get("trainingSet");
497
if (source != null && source instanceof EventConstraints) {
498
if (!((EventConstraints)source).eventGeneratable("trainingSet")) {
502
source = m_listenees.get("dataSet");
503
if (source != null && source instanceof EventConstraints) {
504
if (!((EventConstraints)source).eventGeneratable("dataSet")) {