source: src/main/java/weka/gui/beans/Saver.java @ 8

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

Import di weka.

File size: 17.7 KB
RevLine 
[4]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 *    Saver.java
19 *    Copyright (C) 2004 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.beans;
24
25import java.io.IOException;
26import java.io.ObjectInputStream;
27
28import weka.core.Environment;
29import weka.core.EnvironmentHandler;
30import weka.core.Instances;
31import weka.core.OptionHandler;
32import weka.core.Utils;
33import weka.core.converters.ArffSaver;
34import weka.core.converters.DatabaseConverter;
35import weka.core.converters.DatabaseSaver;
36
37/**
38 * Saves data sets using weka.core.converter classes
39 *
40 * @author <a href="mailto:mutter@cs.waikato.ac.nz">Stefan Mutter</a>
41 * @version $Revision: 5738 $
42 *
43 */
44public class Saver
45  extends AbstractDataSink
46  implements WekaWrapper, EnvironmentHandler {
47
48  /** for serialization */
49  private static final long serialVersionUID = 5371716690308950755L;
50
51  /**
52   * Holds the instances to be saved
53   */
54  private Instances m_dataSet;
55
56  /**
57   * Holds the structure
58   */
59  private Instances m_structure;
60
61  /**
62   * Global info for the wrapped loader (if it exists).
63   */
64  protected String m_globalInfo;
65
66  /**
67   * Thread for doing IO in
68   */
69  private transient SaveBatchThread m_ioThread;
70
71  /**
72   * Saver
73   */
74  private weka.core.converters.Saver m_Saver= new ArffSaver();
75
76  /**
77   * The relation name that becomes part of the file name
78   */
79  private String m_fileName;
80 
81  /** Flag indicating that instances will be saved to database. Used because structure information can only be sent after a database has been configured.*/
82  private boolean m_isDBSaver;
83 
84  /**
85   * For file-based savers - if true (default), relation name is used
86   * as the primary part of the filename. If false, then the prefix is
87   * used as the filename. Useful for preventing filenames from getting
88   * too long when there are many filters in a flow.
89   */
90  private boolean m_relationNameForFilename = true;
91 
92  /**
93   * Count for structure available messages
94   */
95  private int m_count;
96 
97  /**
98   * The environment variables.
99   */
100  protected transient Environment m_env;
101 
102  private class SaveBatchThread extends Thread {
103    private DataSink m_DS;
104
105    public SaveBatchThread(DataSink ds) {
106      m_DS= ds;
107    }
108
109    public void run() {
110      try {
111        m_visual.setAnimated();
112               
113        m_Saver.setInstances(m_dataSet);
114        if (m_logger != null) {
115          m_logger.statusMessage(statusMessagePrefix() + "Saving "
116              + m_dataSet.relationName() + "...");
117        }
118        m_Saver.writeBatch();
119        if (m_logger != null) {
120          m_logger.logMessage("[Saver] " + statusMessagePrefix() 
121              + "Save successful.");
122        }
123       
124      } catch (Exception ex) {
125        if (m_logger != null) {
126          m_logger.statusMessage(statusMessagePrefix()
127              + "ERROR (See log for details)");
128          m_logger.logMessage("[Saver] " + statusMessagePrefix()
129              + " problem saving. " 
130              + ex.getMessage());
131        }
132        ex.printStackTrace();
133      } finally {
134        if (Thread.currentThread().isInterrupted()) {
135          if (m_logger != null) {
136            m_logger.logMessage("[Saver] " + statusMessagePrefix()
137                + " Saving interrupted!!");
138          }
139        }
140        if (m_logger != null) {
141          m_logger.statusMessage(statusMessagePrefix() + "Finished.");
142        }
143        block(false);
144        m_visual.setStatic();
145        m_ioThread = null;
146      }
147    }
148  }
149 
150  /**
151   * Function used to stop code that calls acceptTrainingSet. This is
152   * needed as classifier construction is performed inside a separate
153   * thread of execution.
154   *
155   * @param tf a <code>boolean</code> value
156   */
157  private synchronized void block(boolean tf) {
158
159    if (tf) {
160      try {
161        if (m_ioThread.isAlive()) {
162          wait();
163          }
164      } catch (InterruptedException ex) {
165      }
166    } else {
167      notifyAll();
168    }
169  }
170 
171  /**
172   * Returns true if. at this time, the bean is busy with some
173   * (i.e. perhaps a worker thread is performing some calculation).
174   *
175   * @return true if the bean is busy.
176   */
177  public boolean isBusy() {
178    return (m_ioThread != null);
179  }
180
181  /**
182   * Global info (if it exists) for the wrapped loader
183   *
184   * @return the global info
185   */
186  public String globalInfo() {
187    return m_globalInfo;
188  }
189
190  /** Contsructor */ 
191  public Saver() {
192    super();
193    setSaver(m_Saver);
194    m_fileName = "";
195    m_dataSet = null;
196    m_count = 0;
197   
198  }
199
200  /**
201   * Set a custom (descriptive) name for this bean
202   *
203   * @param name the name to use
204   */
205  public void setCustomName(String name) {
206    m_visual.setText(name);
207  }
208
209  /**
210   * Get the custom (descriptive) name for this bean (if one has been set)
211   *
212   * @return the custom name (or the default name)
213   */
214  public String getCustomName() {
215    return m_visual.getText();
216  } 
217 
218  /**
219   * Set environment variables to use.
220   *
221   * @param env the environment variables to
222   * use
223   */
224  public void setEnvironment(Environment env) {
225    m_env = env;
226  }
227 
228  /**
229   * Pass the environment variables on the the wrapped saver
230   */
231  private void passEnvOnToSaver() {
232    // set environment variables
233    if (m_Saver instanceof EnvironmentHandler && m_env != null) {
234      ((EnvironmentHandler)m_Saver).setEnvironment(m_env);
235    }
236  }
237
238  /** Set the loader to use
239   * @param saver a Saver
240   */
241  public void setSaver(weka.core.converters.Saver saver) {
242    boolean loadImages = true;
243    if (saver.getClass().getName().
244        compareTo(m_Saver.getClass().getName()) == 0) {
245      loadImages = false;
246    }
247    m_Saver = saver;
248    String saverName = saver.getClass().toString();
249    saverName = saverName.substring(saverName.
250                                      lastIndexOf('.')+1, 
251                                      saverName.length());
252    if (loadImages) {
253
254      if (!m_visual.loadIcons(BeanVisual.ICON_PATH+saverName+".gif",
255                            BeanVisual.ICON_PATH+saverName+"_animated.gif")) {
256        useDefaultVisual();
257      }
258    }
259    m_visual.setText(saverName);
260
261   
262    // get global info
263    m_globalInfo = KnowledgeFlowApp.getGlobalInfo(m_Saver);
264    if(m_Saver instanceof DatabaseConverter)
265        m_isDBSaver = true;
266    else
267        m_isDBSaver = false;
268  }
269
270  /**
271   * makes sure that the filename is valid, i.e., replaces slashes,
272   * backslashes and colons with underscores ("_"). Also try to prevent
273   * filename from becoming insanely long by removing package part
274   * of class names.
275   *
276   * @param filename    the filename to cleanse
277   * @return            the cleansed filename
278   */
279  protected String sanitizeFilename(String filename) {
280    filename = filename.replaceAll("\\\\", "_").replaceAll(":", "_").replaceAll("/", "_");
281    filename = Utils.removeSubstring(filename, "weka.filters.supervised.instance.");
282    filename = Utils.removeSubstring(filename, "weka.filters.supervised.attribute.");
283    filename = Utils.removeSubstring(filename, "weka.filters.unsupervised.instance.");
284    filename = Utils.removeSubstring(filename, "weka.filters.unsupervised.attribute.");
285    filename = Utils.removeSubstring(filename, "weka.clusterers.");
286    filename = Utils.removeSubstring(filename, "weka.associations.");
287    filename = Utils.removeSubstring(filename, "weka.attributeSelection.");
288    filename = Utils.removeSubstring(filename, "weka.estimators.");
289    filename = Utils.removeSubstring(filename, "weka.datagenerators.");
290   
291    if (!m_isDBSaver && !m_relationNameForFilename) {
292      filename = "";
293      try {
294        if (m_Saver.filePrefix().equals("")) {
295          m_Saver.setFilePrefix("no-name");
296        }
297      } catch (Exception ex) {
298        System.err.println(ex);
299      }
300    }
301
302    return filename;
303  }
304 
305  /** Method reacts to a dataset event and starts the writing process in batch mode
306   * @param e a dataset event
307   */ 
308  public synchronized void acceptDataSet(DataSetEvent e) {
309 
310      passEnvOnToSaver();
311      m_fileName = sanitizeFilename(e.getDataSet().relationName());
312      m_dataSet = e.getDataSet();
313      if(e.isStructureOnly() && m_isDBSaver && ((DatabaseSaver)m_Saver).getRelationForTableName()){//
314          ((DatabaseSaver)m_Saver).setTableName(m_fileName);
315      }
316      if(!e.isStructureOnly()){
317          if(!m_isDBSaver){
318            try{
319                m_Saver.setDirAndPrefix(m_fileName,"");
320            }catch (Exception ex){
321                System.out.println(ex);
322            }
323          }
324          saveBatch();
325          System.out.println("...relation "+ m_fileName +" saved.");
326      }
327  }
328 
329  /**
330   * Method reacts to a threshold data event ans starts the writing process
331   * in batch mode.
332   *
333   * @param e threshold data event.
334   */
335  public synchronized void acceptDataSet(ThresholdDataEvent e) {
336    passEnvOnToSaver();
337    m_fileName = sanitizeFilename(e.getDataSet().getPlotInstances().relationName());
338    m_dataSet = e.getDataSet().getPlotInstances();
339   
340    if(m_isDBSaver && ((DatabaseSaver)m_Saver).getRelationForTableName()){//
341      ((DatabaseSaver)m_Saver).setTableName(m_fileName);
342    }
343
344    if(!m_isDBSaver){
345      try{
346        m_Saver.setDirAndPrefix(m_fileName,"");
347      }catch (Exception ex){
348        System.out.println(ex);
349      }
350    }
351    saveBatch();
352    System.out.println("...relation "+ m_fileName +" saved.");
353  }
354 
355  /** Method reacts to a test set event and starts the writing process in batch mode
356   * @param e test set event
357   */ 
358  public synchronized void acceptTestSet(TestSetEvent e) {
359 
360      passEnvOnToSaver();
361      m_fileName = sanitizeFilename(e.getTestSet().relationName());
362      m_dataSet = e.getTestSet();
363      if(e.isStructureOnly() && m_isDBSaver && ((DatabaseSaver)m_Saver).getRelationForTableName()){
364          ((DatabaseSaver)m_Saver).setTableName(m_fileName);
365      }
366      if(!e.isStructureOnly()){
367          if(!m_isDBSaver){
368            try{
369                m_Saver.setDirAndPrefix(m_fileName,"_test_"+e.getSetNumber()+"_of_"+e.getMaxSetNumber());
370            }catch (Exception ex){
371                System.out.println(ex);
372            }
373          }
374          else{
375              String setName = ((DatabaseSaver)m_Saver).getTableName();
376              setName = setName.replaceFirst("_[tT][eE][sS][tT]_[0-9]+_[oO][fF]_[0-9]+","");
377              ((DatabaseSaver)m_Saver).setTableName(setName+"_test_"+e.getSetNumber()+"_of_"+e.getMaxSetNumber());
378          }
379          saveBatch();
380          System.out.println("... test set "+e.getSetNumber()+" of "+e.getMaxSetNumber()+" for relation "+ m_fileName +" saved.");
381      }
382  }
383 
384  /** Method reacts to a training set event and starts the writing process in batch
385   * mode
386   * @param e a training set event
387   */ 
388  public synchronized void acceptTrainingSet(TrainingSetEvent e) {
389 
390      passEnvOnToSaver();
391      m_fileName = sanitizeFilename(e.getTrainingSet().relationName());
392      m_dataSet = e.getTrainingSet();
393      if(e.isStructureOnly() && m_isDBSaver && ((DatabaseSaver)m_Saver).getRelationForTableName()){
394           ((DatabaseSaver)m_Saver).setTableName(m_fileName);
395      }
396      if(!e.isStructureOnly()){
397          if(!m_isDBSaver){
398            try{
399                m_Saver.setDirAndPrefix(m_fileName,"_training_"+e.getSetNumber()+"_of_"+e.getMaxSetNumber());
400            }catch (Exception ex){
401                System.out.println(ex);
402            }
403          }
404          else{
405              String setName = ((DatabaseSaver)m_Saver).getTableName();
406              setName = setName.replaceFirst("_[tT][rR][aA][iI][nN][iI][nN][gG]_[0-9]+_[oO][fF]_[0-9]+","");
407              ((DatabaseSaver)m_Saver).setTableName(setName+"_training_"+e.getSetNumber()+"_of_"+e.getMaxSetNumber());
408          }
409          saveBatch();
410          System.out.println("... training set "+e.getSetNumber()+" of "+e.getMaxSetNumber()+" for relation "+ m_fileName +" saved.");
411      }
412  }
413 
414  /** Saves instances in batch mode */ 
415  public synchronized void saveBatch(){
416 
417      m_Saver.setRetrieval(m_Saver.BATCH);
418/*      String visText = this.getName();
419      try {
420        visText = (m_fileName.length() > 0) ? m_fileName : m_Saver.filePrefix();
421      } catch (Exception ex) {       
422      }
423      m_visual.setText(visText); */
424      m_ioThread = new SaveBatchThread(Saver.this);
425      m_ioThread.setPriority(Thread.MIN_PRIORITY);
426      m_ioThread.start();
427      block(true);
428  }
429 
430  /** Methods reacts to instance events and saves instances incrementally.
431   * If the instance to save is null, the file is closed and the saving process is
432   * ended.
433   * @param e instance event
434   */ 
435  public synchronized void acceptInstance(InstanceEvent e) {
436     
437     
438      if(e.getStatus() == e.FORMAT_AVAILABLE){
439        m_Saver.setRetrieval(m_Saver.INCREMENTAL);
440        m_structure = e.getStructure();
441        m_fileName = sanitizeFilename(m_structure.relationName());
442        m_Saver.setInstances(m_structure);
443        if(m_isDBSaver)
444            if(((DatabaseSaver)m_Saver).getRelationForTableName())
445                ((DatabaseSaver)m_Saver).setTableName(m_fileName);
446      }
447      if(e.getStatus() == e.INSTANCE_AVAILABLE){
448        m_visual.setAnimated();
449        if(m_count == 0){
450            passEnvOnToSaver();
451            if(!m_isDBSaver){
452                try{
453                    m_Saver.setDirAndPrefix(m_fileName,"");
454                }catch (Exception ex){
455                    System.out.println(ex);
456                    m_visual.setStatic();
457                }
458            }
459            m_count ++;
460        }
461        try{
462/*          String visText = this.getName();
463          visText = (m_fileName.length() > 0) ? m_fileName : m_Saver.filePrefix();
464            m_visual.setText(m_fileName); */
465            m_Saver.writeIncremental(e.getInstance());
466        } catch (Exception ex) {
467            m_visual.setStatic();
468            System.err.println("Instance "+e.getInstance() +" could not been saved");
469            ex.printStackTrace();
470        }
471      }
472      if(e.getStatus() == e.BATCH_FINISHED){
473        try{ 
474            m_Saver.writeIncremental(e.getInstance());
475            m_Saver.writeIncremental(null);
476            //m_firstNotice = true;
477            m_visual.setStatic();
478            System.out.println("...relation "+ m_fileName +" saved.");
479/*            String visText = this.getName();
480            visText = (m_fileName.length() > 0) ? m_fileName : m_Saver.filePrefix();
481            m_visual.setText(visText); */     
482            m_count = 0;
483        } catch (Exception ex) {
484            m_visual.setStatic();
485            System.err.println("File could not have been closed.");
486            ex.printStackTrace();
487        }
488      }
489  }
490 
491 
492
493  /**
494   * Get the saver
495   *
496   * @return a <code>weka.core.converters.Saver</code> value
497   */
498  public weka.core.converters.Saver getSaver() {
499    return m_Saver;
500  }
501
502  /**
503   * Set the saver
504   *
505   * @param algorithm a Saver
506   */
507  public void setWrappedAlgorithm(Object algorithm) 
508    {
509
510    if (!(algorithm instanceof weka.core.converters.Saver)) { 
511      throw new IllegalArgumentException(algorithm.getClass()+" : incorrect "
512                                         +"type of algorithm (Loader)");
513    }
514    setSaver((weka.core.converters.Saver)algorithm);
515  }
516
517  /**
518   * Get the saver
519   *
520   * @return a Saver
521   */
522  public Object getWrappedAlgorithm() {
523    return getSaver();
524  }
525 
526  /**
527   * Set whether to use the relation name as the primary part
528   * of the filename. If false, then the prefix becomes the filename.
529   *
530   * @param r true if the relation name is to be part of the filename.
531   */
532  public void setRelationNameForFilename(boolean r) {
533    m_relationNameForFilename = r;
534  }
535 
536  /**
537   * Get whether the relation name is the primary part of the filename.
538   *
539   * @return true if the relation name is part of the filename.
540   */
541  public boolean getRelationNameForFilename() {
542    return m_relationNameForFilename;
543  }
544
545  /** Stops the bean */ 
546  public void stop() {
547    // tell the listenee (upstream bean) to stop
548    if (m_listenee instanceof BeanCommon) {
549      ((BeanCommon)m_listenee).stop();
550    }
551   
552    // stop the io thread
553    if (m_ioThread != null) {
554      m_ioThread.interrupt();
555      m_ioThread.stop();
556      m_ioThread = null;
557      m_visual.setStatic();
558    }
559  }
560 
561  private String statusMessagePrefix() {
562    return getCustomName() + "$" + hashCode() + "|"
563    + ((m_Saver instanceof OptionHandler) 
564        ? Utils.joinOptions(((OptionHandler)m_Saver).getOptions()) + "|"
565            : "");
566  }
567 
568  // Custom de-serialization in order to set default
569  // environment variables on de-serialization
570  private void readObject(ObjectInputStream aStream) 
571    throws IOException, ClassNotFoundException {
572    aStream.defaultReadObject();
573   
574    // set a default environment to use
575    m_env = Environment.getSystemWide();
576  }
577 
578 
579  /** The main method for testing
580   * @param args
581   */ 
582  public static void main(String [] args) {
583    try {
584      final javax.swing.JFrame jf = new javax.swing.JFrame();
585      jf.getContentPane().setLayout(new java.awt.BorderLayout());
586
587      final Saver tv = new Saver();
588
589      jf.getContentPane().add(tv, java.awt.BorderLayout.CENTER);
590      jf.addWindowListener(new java.awt.event.WindowAdapter() {
591        public void windowClosing(java.awt.event.WindowEvent e) {
592          jf.dispose();
593          System.exit(0);
594        }
595      });
596      jf.setSize(800,600);
597      jf.setVisible(true);
598    } catch (Exception ex) {
599      ex.printStackTrace();
600    }
601  }
602 
603}
604
Note: See TracBrowser for help on using the repository browser.