source: src/main/java/weka/gui/arffviewer/ArffTableModel.java @ 13

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

Import di weka.

File size: 23.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 * ArffTableModel.java
19 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.arffviewer;
24
25import weka.core.Attribute;
26import weka.core.Instance;
27import weka.core.Instances;
28import weka.core.Undoable;
29import weka.core.Utils;
30import weka.core.converters.AbstractFileLoader;
31import weka.core.converters.ConverterUtils;
32import weka.filters.Filter;
33import weka.filters.unsupervised.attribute.Reorder;
34import weka.gui.ComponentHelper;
35
36import java.io.BufferedInputStream;
37import java.io.BufferedOutputStream;
38import java.io.File;
39import java.io.FileInputStream;
40import java.io.FileOutputStream;
41import java.io.ObjectInputStream;
42import java.io.ObjectOutputStream;
43import java.util.Arrays;
44import java.util.HashSet;
45import java.util.Iterator;
46import java.util.Vector;
47
48import javax.swing.JOptionPane;
49import javax.swing.event.TableModelEvent;
50import javax.swing.event.TableModelListener;
51import javax.swing.table.TableModel;
52
53/**
54 * The model for the Arff-Viewer.
55 *
56 *
57 * @author FracPete (fracpete at waikato dot ac dot nz)
58 * @version $Revision: 5987 $
59 */
60public class ArffTableModel 
61  implements TableModel, Undoable {
62 
63  /** the listeners */
64  private HashSet m_Listeners;
65  /** the data */
66  private Instances m_Data;
67  /** whether notfication is enabled */
68  private boolean m_NotificationEnabled;
69  /** whether undo is active */
70  private boolean m_UndoEnabled;
71  /** whether to ignore changes, i.e. not adding to undo history */
72  private boolean m_IgnoreChanges;
73  /** the undo list (contains temp. filenames) */
74  private Vector m_UndoList;
75  /** whether the table is read-only */
76  private boolean m_ReadOnly;
77 
78  /**
79   * performs some initialization
80   */
81  private ArffTableModel() {
82    super();
83   
84    m_Listeners           = new HashSet();
85    m_Data                = null;
86    m_NotificationEnabled = true;
87    m_UndoList            = new Vector();
88    m_IgnoreChanges       = false;
89    m_UndoEnabled         = true;
90    m_ReadOnly            = false;
91  }
92 
93  /**
94   * initializes the object and loads the given file
95   *
96   * @param filename    the file to load
97   */
98  public ArffTableModel(String filename) {
99    this();
100   
101    if ( (filename != null) && (!filename.equals("")) )
102      loadFile(filename);
103  }
104 
105  /**
106   * initializes the model with the given data
107   *
108   * @param data        the data to use
109   */
110  public ArffTableModel(Instances data) {
111    this();
112   
113    this.m_Data = data;
114  }
115
116  /**
117   * returns whether the notification of changes is enabled
118   *
119   * @return            true if notification of changes is enabled
120   */
121  public boolean isNotificationEnabled() {
122    return m_NotificationEnabled;
123  }
124 
125  /**
126   * sets whether the notification of changes is enabled
127   *
128   * @param enabled     enables/disables the notification
129   */
130  public void setNotificationEnabled(boolean enabled) {
131    m_NotificationEnabled = enabled;
132  }
133
134  /**
135   * returns whether undo support is enabled
136   *
137   * @return            true if undo support is enabled
138   */
139  public boolean isUndoEnabled() {
140    return m_UndoEnabled;
141  }
142 
143  /**
144   * sets whether undo support is enabled
145   *
146   * @param enabled     whether to enable/disable undo support
147   */
148  public void setUndoEnabled(boolean enabled) {
149    m_UndoEnabled = enabled;
150  }
151
152  /**
153   * returns whether the model is read-only
154   *
155   * @return            true if model is read-only
156   */
157  public boolean isReadOnly() {
158    return m_ReadOnly;
159  }
160 
161  /**
162   * sets whether the model is read-only
163   *
164   * @param value       if true the model is set to read-only
165   */
166  public void setReadOnly(boolean value) {
167    m_ReadOnly = value;
168  }
169 
170  /**
171   * loads the specified ARFF file
172   *
173   * @param filename    the file to load
174   */
175  private void loadFile(String filename) {
176    AbstractFileLoader          loader;
177   
178    loader = ConverterUtils.getLoaderForFile(filename);
179   
180    if (loader != null) {
181      try {
182        loader.setFile(new File(filename));
183        m_Data = loader.getDataSet();
184      }
185      catch (Exception e) {
186        ComponentHelper.showMessageBox(
187            null, 
188            "Error loading file...", 
189            e.toString(), 
190            JOptionPane.OK_CANCEL_OPTION,
191            JOptionPane.ERROR_MESSAGE );
192        System.out.println(e);
193        m_Data = null;
194      }
195    }
196  }
197 
198  /**
199   * sets the data
200   *
201   * @param data        the data to use
202   */
203  public void setInstances(Instances data) {
204    m_Data = data;
205  }
206 
207  /**
208   * returns the data
209   *
210   * @return            the current data
211   */
212  public Instances getInstances() {
213    return m_Data;
214  }
215 
216  /**
217   * returns the attribute at the given index, can be NULL if not an attribute
218   * column
219   *
220   * @param columnIndex         the index of the column
221   * @return                    the attribute at the position
222   */
223  public Attribute getAttributeAt(int columnIndex) {
224    if ( (columnIndex > 0) && (columnIndex < getColumnCount()) )
225      return m_Data.attribute(columnIndex - 1);
226    else
227      return null;
228  }
229 
230  /**
231   * returns the TYPE of the attribute at the given position
232   *
233   * @param columnIndex         the index of the column
234   * @return                    the attribute type
235   */
236  public int getType(int columnIndex) {
237    return getType(0, columnIndex);
238  }
239 
240  /**
241   * returns the TYPE of the attribute at the given position
242   *
243   * @param rowIndex            the index of the row
244   * @param columnIndex         the index of the column
245   * @return                    the attribute type
246   */
247  public int getType(int rowIndex, int columnIndex) {
248    int            result;
249   
250    result = Attribute.STRING;
251   
252    if (    (rowIndex >= 0) && (rowIndex < getRowCount())
253         && (columnIndex > 0) && (columnIndex < getColumnCount()) )
254      result = m_Data.instance(rowIndex).attribute(columnIndex - 1).type();
255   
256    return result;
257  }
258 
259  /**
260   * deletes the attribute at the given col index. notifies the listeners.
261   *
262   * @param columnIndex     the index of the attribute to delete
263   */
264  public void deleteAttributeAt(int columnIndex) {
265    deleteAttributeAt(columnIndex, true);
266  }
267 
268  /**
269   * deletes the attribute at the given col index
270   *
271   * @param columnIndex     the index of the attribute to delete
272   * @param notify          whether to notify the listeners
273   */
274  public void deleteAttributeAt(int columnIndex, boolean notify) {
275    if ( (columnIndex > 0) && (columnIndex < getColumnCount()) ) {
276      if (!m_IgnoreChanges)
277        addUndoPoint();
278      m_Data.deleteAttributeAt(columnIndex - 1);
279      if (notify) 
280        notifyListener(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
281    }
282  }
283 
284  /**
285   * deletes the attributes at the given indices
286   *
287   * @param columnIndices       the column indices
288   */
289  public void deleteAttributes(int[] columnIndices) {
290    int            i;
291   
292    Arrays.sort(columnIndices);
293   
294    addUndoPoint();
295
296    m_IgnoreChanges = true;
297    for (i = columnIndices.length - 1; i >= 0; i--)
298      deleteAttributeAt(columnIndices[i], false);
299    m_IgnoreChanges = false;
300
301    notifyListener(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
302  }
303 
304  /**
305   * renames the attribute at the given col index
306   *
307   * @param columnIndex         the index of the column
308   * @param newName             the new name of the attribute
309   */
310  public void renameAttributeAt(int columnIndex, String newName) {
311    if ( (columnIndex > 0) && (columnIndex < getColumnCount()) ) {
312      addUndoPoint();
313      m_Data.renameAttribute(columnIndex - 1, newName);
314      notifyListener(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
315    }
316  }
317 
318  /**
319   * sets the attribute at the given col index as the new class attribute, i.e.
320   * it moves it to the end of the attributes
321   *
322   * @param columnIndex         the index of the column
323   */
324  public void attributeAsClassAt(int columnIndex) {
325    Reorder     reorder;
326    String      order;
327    int         i;
328   
329    if ( (columnIndex > 0) && (columnIndex < getColumnCount()) ) {
330      addUndoPoint();
331     
332      try {
333        // build order string (1-based!)
334        order = "";
335        for (i = 1; i < m_Data.numAttributes() + 1; i++) {
336          // skip new class
337          if (i == columnIndex)
338            continue;
339         
340          if (!order.equals(""))
341            order += ",";
342          order += Integer.toString(i);
343        }
344        if (!order.equals(""))
345          order += ",";
346        order += Integer.toString(columnIndex);
347       
348        // process data
349        reorder = new Reorder();
350        reorder.setAttributeIndices(order);
351        reorder.setInputFormat(m_Data);
352        m_Data = Filter.useFilter(m_Data, reorder);
353       
354        // set class index
355        m_Data.setClassIndex(m_Data.numAttributes() - 1);
356      }
357      catch (Exception e) {
358        e.printStackTrace();
359        undo();
360      }
361     
362      notifyListener(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
363    }
364  }
365 
366  /**
367   * deletes the instance at the given index
368   *
369   * @param rowIndex            the index of the row
370   */
371  public void deleteInstanceAt(int rowIndex) {
372    deleteInstanceAt(rowIndex, true);
373  }
374 
375  /**
376   * deletes the instance at the given index
377   *
378   * @param rowIndex            the index of the row
379   * @param notify              whether to notify the listeners
380   */
381  public void deleteInstanceAt(int rowIndex, boolean notify) {
382    if ( (rowIndex >= 0) && (rowIndex < getRowCount()) ) {
383      if (!m_IgnoreChanges)
384        addUndoPoint();
385      m_Data.delete(rowIndex);
386      if (notify)
387        notifyListener(
388            new TableModelEvent(
389                this, rowIndex, rowIndex, 
390                TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
391    }
392  }
393 
394  /**
395   * deletes the instances at the given positions
396   *
397   * @param rowIndices          the indices to delete
398   */
399  public void deleteInstances(int[] rowIndices) {
400    int               i;
401   
402    Arrays.sort(rowIndices);
403   
404    addUndoPoint();
405   
406    m_IgnoreChanges = true;
407    for (i = rowIndices.length - 1; i >= 0; i--)
408      deleteInstanceAt(rowIndices[i], false);
409    m_IgnoreChanges = false;
410
411    notifyListener(
412        new TableModelEvent(
413            this, rowIndices[0], rowIndices[rowIndices.length - 1], 
414            TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
415  }
416 
417  /**
418   * sorts the instances via the given attribute
419   *
420   * @param columnIndex         the index of the column
421   */
422  public void sortInstances(int columnIndex) {
423    if ( (columnIndex > 0) && (columnIndex < getColumnCount()) ) {
424      addUndoPoint();
425      m_Data.sort(columnIndex - 1);
426      notifyListener(new TableModelEvent(this));
427    }
428  }
429 
430  /**
431   * returns the column of the given attribute name, -1 if not found
432   *
433   * @param name                the name of the attribute
434   * @return                    the column index or -1 if not found
435   */
436  public int getAttributeColumn(String name) {
437    int            i;
438    int            result;
439   
440    result = -1;
441   
442    for (i = 0; i < m_Data.numAttributes(); i++) {
443      if (m_Data.attribute(i).name().equals(name)) {
444        result = i + 1;
445        break;
446      }
447    }
448   
449    return result;
450  }
451 
452  /**
453   * returns the most specific superclass for all the cell values in the
454   * column (always String)
455   *
456   * @param columnIndex         the column index
457   * @return                    the class of the column
458   */
459  public Class getColumnClass(int columnIndex) {
460    Class       result;
461   
462    result = null;
463   
464    if ( (columnIndex >= 0) && (columnIndex < getColumnCount()) ) {
465      if (columnIndex == 0)
466        result = Integer.class;
467      else if (getType(columnIndex) == Attribute.NUMERIC)
468        result = Double.class;
469      else
470        result = String.class;   // otherwise no input of "?"!!!
471    }
472   
473    return result;
474  }
475 
476  /**
477   * returns the number of columns in the model
478   *
479   * @return            the number of columns
480   */
481  public int getColumnCount() {
482    int         result;
483   
484    result = 1;
485    if (m_Data != null)
486      result += m_Data.numAttributes();
487   
488    return result;
489  }
490 
491  /**
492   * checks whether the column represents the class or not
493   *
494   * @param columnIndex         the index of the column
495   * @return                    true if the column is the class attribute
496   */
497  private boolean isClassIndex(int columnIndex) {
498    boolean        result;
499    int            index;
500   
501    index  = m_Data.classIndex();
502    result =    ((index == - 1) && (m_Data.numAttributes() == columnIndex))
503             || (index == columnIndex - 1);
504   
505    return result;
506  }
507 
508  /**
509   * returns the name of the column at columnIndex
510   *
511   * @param columnIndex         the index of the column
512   * @return                    the name of the column
513   */
514  public String getColumnName(int columnIndex) {
515    String      result;
516   
517    result = "";
518   
519    if ( (columnIndex >= 0) && (columnIndex < getColumnCount()) ) {
520      if (columnIndex == 0) {
521        result = "<html><center>No.<br><font size=\"-2\">&nbsp;</font></center></html>";
522      }
523      else {
524        if (m_Data != null) {
525          if ( (columnIndex - 1 < m_Data.numAttributes()) ) {
526            result = "<html><center>";
527            // name
528            if (isClassIndex(columnIndex))
529              result +=   "<b>" 
530                + m_Data.attribute(columnIndex - 1).name() 
531                + "</b>";
532            else
533              result += m_Data.attribute(columnIndex - 1).name();
534           
535            // attribute type
536            switch (getType(columnIndex)) {
537              case Attribute.DATE: 
538                result += "<br><font size=\"-2\">Date</font>";
539                break;
540              case Attribute.NOMINAL:
541                result += "<br><font size=\"-2\">Nominal</font>";
542                break;
543              case Attribute.STRING:
544                result += "<br><font size=\"-2\">String</font>";
545                break;
546              case Attribute.NUMERIC:
547                result += "<br><font size=\"-2\">Numeric</font>";
548                break;
549              case Attribute.RELATIONAL:
550                result += "<br><font size=\"-2\">Relational</font>";
551                break;
552              default:
553                result += "<br><font size=\"-2\">???</font>";
554            }
555           
556            result += "</center></html>";
557          }
558        }
559      }
560    }
561   
562    return result;
563  }
564 
565  /**
566   * returns the number of rows in the model
567   *
568   * @return            the number of rows
569   */
570  public int getRowCount() {
571    if (m_Data == null)
572      return 0;
573    else
574      return m_Data.numInstances(); 
575  }
576 
577  /**
578   * checks whether the value at the given position is missing
579   *
580   * @param rowIndex            the row index
581   * @param columnIndex         the column index
582   * @return                    true if the value at the position is missing
583   */
584  public boolean isMissingAt(int rowIndex, int columnIndex) {
585    boolean           result;
586   
587    result = false;
588   
589    if (    (rowIndex >= 0) && (rowIndex < getRowCount())
590         && (columnIndex > 0) && (columnIndex < getColumnCount()) )
591      result = (m_Data.instance(rowIndex).isMissing(columnIndex - 1));
592   
593    return result;
594  }
595 
596  /**
597   * returns the double value of the underlying Instances object at the
598   * given position, -1 if out of bounds
599   *
600   * @param rowIndex            the row index
601   * @param columnIndex         the column index
602   * @return                    the underlying value in the Instances object
603   */
604  public double getInstancesValueAt(int rowIndex, int columnIndex) {
605    double      result;
606   
607    result = -1;
608   
609    if (    (rowIndex >= 0) && (rowIndex < getRowCount())
610         && (columnIndex > 0) && (columnIndex < getColumnCount()) )
611      result = m_Data.instance(rowIndex).value(columnIndex - 1);
612   
613    return result;
614  }
615 
616  /**
617   * returns the value for the cell at columnindex and rowIndex
618   *
619   * @param rowIndex            the row index
620   * @param columnIndex         the column index
621   * @return                    the value at the position
622   */
623  public Object getValueAt(int rowIndex, int columnIndex) {
624    Object            result;
625    String            tmp;
626   
627    result = null;
628   
629    if (    (rowIndex >= 0) && (rowIndex < getRowCount())
630        && (columnIndex >= 0) && (columnIndex < getColumnCount()) ) {
631      if (columnIndex == 0) {
632        result = new Integer(rowIndex + 1);
633      }
634      else {
635        if (isMissingAt(rowIndex, columnIndex)) {
636          result = null;
637        }
638        else {
639          switch (getType(columnIndex)) {
640            case Attribute.DATE: 
641            case Attribute.NOMINAL:
642            case Attribute.STRING:
643            case Attribute.RELATIONAL:
644              result = m_Data.instance(rowIndex).stringValue(columnIndex - 1);
645              break;
646            case Attribute.NUMERIC:
647              result = new Double(m_Data.instance(rowIndex).value(columnIndex - 1));
648              break;
649            default:
650              result = "-can't display-";
651          }
652        }
653      }
654    }
655   
656    if (getType(columnIndex) != Attribute.NUMERIC) {
657      if (result != null) {
658        // does it contain "\n" or "\r"? -> replace with red html tag
659        tmp = result.toString();
660        if ( (tmp.indexOf("\n") > -1) || (tmp.indexOf("\r") > -1) ) {
661          tmp    = tmp.replaceAll("\\r\\n", "<font color=\"red\"><b>\\\\r\\\\n</b></font>");
662          tmp    = tmp.replaceAll("\\r", "<font color=\"red\"><b>\\\\r</b></font>");
663          tmp    = tmp.replaceAll("\\n", "<font color=\"red\"><b>\\\\n</b></font>");
664          result = "<html>" + tmp + "</html>";
665        }
666      }
667    }
668   
669    return result;
670  }
671 
672  /**
673   * returns true if the cell at rowindex and columnindexis editable
674   *
675   * @param rowIndex            the index of the row
676   * @param columnIndex         the index of the column
677   * @return                    true if the cell is editable
678   */
679  public boolean isCellEditable(int rowIndex, int columnIndex) {
680    return (columnIndex > 0) && !isReadOnly();
681  }
682 
683  /**
684   * sets the value in the cell at columnIndex and rowIndex to aValue.
685   * but only the value and the value can be changed
686   *
687   * @param aValue              the new value
688   * @param rowIndex            the row index
689   * @param columnIndex         the column index
690   */
691  public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
692    setValueAt(aValue, rowIndex, columnIndex, true);
693  }
694 
695  /**
696   * sets the value in the cell at columnIndex and rowIndex to aValue.
697   * but only the value and the value can be changed
698   *
699   * @param aValue              the new value
700   * @param rowIndex            the row index
701   * @param columnIndex         the column index
702   * @param notify              whether to notify the listeners
703   */
704  public void setValueAt(Object aValue, int rowIndex, int columnIndex, boolean notify) {
705    int            type;
706    int            index;
707    String         tmp;
708    Instance       inst;
709    Attribute      att;
710    Object         oldValue;
711   
712    if (!m_IgnoreChanges)
713      addUndoPoint();
714   
715    oldValue = getValueAt(rowIndex, columnIndex);
716    type     = getType(rowIndex, columnIndex);
717    index    = columnIndex - 1;
718    inst     = m_Data.instance(rowIndex);
719    att      = inst.attribute(index);
720   
721    // missing?
722    if (aValue == null) {
723      inst.setValue(index, Utils.missingValue());
724    }
725    else {
726      tmp = aValue.toString();
727     
728      switch (type) {
729        case Attribute.DATE:
730          try {
731            att.parseDate(tmp);
732            inst.setValue(index, att.parseDate(tmp));
733          }
734          catch (Exception e) {
735            // ignore
736          }
737          break;
738     
739        case Attribute.NOMINAL:
740          if (att.indexOfValue(tmp) > -1)
741            inst.setValue(index, att.indexOfValue(tmp));
742          break;
743     
744        case Attribute.STRING:
745          inst.setValue(index, tmp);
746          break;
747     
748        case Attribute.NUMERIC:
749          try {
750            Double.parseDouble(tmp);
751            inst.setValue(index, Double.parseDouble(tmp));
752          }
753          catch (Exception e) {
754            // ignore
755          }
756          break;
757         
758        case Attribute.RELATIONAL:
759          try {
760            inst.setValue(index, inst.attribute(index).addRelation((Instances) aValue));
761          }
762          catch (Exception e) {
763            // ignore
764          }
765          break;
766         
767        default:
768          throw new IllegalArgumentException("Unsupported Attribute type: " + type + "!");
769      }
770    }
771   
772    // notify only if the value has changed!
773    if (notify && (!("" + oldValue).equals("" + aValue)) )
774        notifyListener(new TableModelEvent(this, rowIndex, columnIndex));
775  }
776 
777  /**
778   * adds a listener to the list that is notified each time a change to data
779   * model occurs
780   *
781   * @param l           the listener to add
782   */
783  public void addTableModelListener(TableModelListener l) {
784    m_Listeners.add(l);
785  }
786 
787  /**
788   * removes a listener from the list that is notified each time a change to
789   * the data model occurs
790   *
791   * @param l           the listener to remove
792   */
793  public void removeTableModelListener(TableModelListener l) {
794    m_Listeners.remove(l);
795  }
796 
797  /**
798   * notfies all listener of the change of the model
799   *
800   * @param e           the event to send to the listeners
801   */
802  public void notifyListener(TableModelEvent e) {
803    Iterator                iter;
804    TableModelListener      l;
805
806    // is notification enabled?
807    if (!isNotificationEnabled())
808      return;
809   
810    iter = m_Listeners.iterator();
811    while (iter.hasNext()) {
812      l = (TableModelListener) iter.next();
813      l.tableChanged(e);
814    }
815  }
816
817  /**
818   * removes the undo history
819   */
820  public void clearUndo() {
821    m_UndoList = new Vector();
822  }
823 
824  /**
825   * returns whether an undo is possible, i.e. whether there are any undo points
826   * saved so far
827   *
828   * @return returns TRUE if there is an undo possible
829   */
830  public boolean canUndo() {
831    return !m_UndoList.isEmpty();
832  }
833 
834  /**
835   * undoes the last action
836   */
837  public void undo() {
838    File                  tempFile;
839    Instances             inst;
840    ObjectInputStream     ooi;
841   
842    if (canUndo()) {
843      // load file
844      tempFile = (File) m_UndoList.get(m_UndoList.size() - 1);
845      try {
846        // read serialized data
847        ooi = new ObjectInputStream(new BufferedInputStream(new FileInputStream(tempFile)));
848        inst = (Instances) ooi.readObject();
849        ooi.close();
850       
851        // set instances
852        setInstances(inst);
853        notifyListener(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
854        notifyListener(new TableModelEvent(this));
855      }
856      catch (Exception e) {
857        e.printStackTrace();
858      }
859      tempFile.delete();
860     
861      // remove from undo
862      m_UndoList.remove(m_UndoList.size() - 1);
863    }
864  }
865 
866  /**
867   * adds an undo point to the undo history, if the undo support is enabled
868   * @see #isUndoEnabled()
869   * @see #setUndoEnabled(boolean)
870   */
871  public void addUndoPoint() {
872    File                  tempFile;
873    ObjectOutputStream    oos;
874
875    // undo support currently on?
876    if (!isUndoEnabled())
877      return;
878   
879    if (getInstances() != null) {
880      try {
881        // temp. filename
882        tempFile = File.createTempFile("arffviewer", null);
883        tempFile.deleteOnExit();
884       
885        // serialize instances
886        oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)));
887        oos.writeObject(getInstances());
888        oos.flush();
889        oos.close();
890       
891        // add to undo list
892        m_UndoList.add(tempFile);
893      }
894      catch (Exception e) {
895        e.printStackTrace();
896      }
897    }
898  }
899}
Note: See TracBrowser for help on using the repository browser.