~ubuntu-branches/ubuntu/precise/weka/precise

« back to all changes in this revision

Viewing changes to weka/core/converters/ConverterUtils.java

  • Committer: Bazaar Package Importer
  • Author(s): Soeren Sonnenburg
  • Date: 2008-02-24 09:18:45 UTC
  • Revision ID: james.westby@ubuntu.com-20080224091845-1l8zy6fm6xipbzsr
Tags: upstream-3.5.7+tut1
ImportĀ upstreamĀ versionĀ 3.5.7+tut1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 *    ConverterUtils.java
 
19
 *    Copyright (C) 2000 University of Waikato, Hamilton, New Zealand
 
20
 *
 
21
 */
 
22
 
 
23
package weka.core.converters;
 
24
 
 
25
import weka.core.ClassDiscovery;
 
26
import weka.core.Instance;
 
27
import weka.core.Instances;
 
28
import weka.gui.GenericObjectEditor;
 
29
import weka.gui.GenericPropertiesCreator;
 
30
 
 
31
import java.io.File;
 
32
import java.io.IOException;
 
33
import java.io.InputStream;
 
34
import java.io.OutputStream;
 
35
import java.io.Serializable;
 
36
import java.io.StreamTokenizer;
 
37
import java.net.URL;
 
38
import java.util.Arrays;
 
39
import java.util.Collections;
 
40
import java.util.Enumeration;
 
41
import java.util.Hashtable;
 
42
import java.util.Properties;
 
43
import java.util.Vector;
 
44
 
 
45
/**
 
46
 * Utility routines for the converter package.
 
47
 *
 
48
 * @author Mark Hall (mhall@cs.waikato.ac.nz)
 
49
 * @author FracPete (fracpete at waikato dot ac dot nz)
 
50
 * @version $Revision: 1.14 $
 
51
 * @see Serializable
 
52
 */
 
53
public class ConverterUtils
 
54
  implements Serializable {
 
55
 
 
56
  /** for serialization. */
 
57
  static final long serialVersionUID = -2460855349276148760L;
 
58
 
 
59
  /**
 
60
   * Helper class for loading data from files and URLs. Via the ConverterUtils
 
61
   * class it determines which converter to use for loading the data into 
 
62
   * memory. If the chosen converter is an incremental one, then the data
 
63
   * will be loaded incrementally, otherwise as batch. In both cases the 
 
64
   * same interface will be used (<code>hasMoreElements</code>, 
 
65
   * <code>nextElement</code>). Before the
 
66
   * data can be read again, one has to call the <code>reset</code> method.
 
67
   * The data source can also be initialized with an Instances object, in 
 
68
   * order to provide a unified interface to files and already loaded datasets.
 
69
   * 
 
70
   * @author FracPete (fracpete at waikato dot ac dot nz)
 
71
   * @version $Revision: 1.14 $
 
72
   * @see #hasMoreElements(Instances)
 
73
   * @see #nextElement(Instances)
 
74
   * @see #reset()
 
75
   * @see DataSink
 
76
   */
 
77
  public static class DataSource
 
78
    implements Serializable {
 
79
    
 
80
    /** for serialization. */
 
81
    private static final long serialVersionUID = -613122395928757332L;
 
82
 
 
83
    /** the file to load. */
 
84
    protected File m_File;
 
85
    
 
86
    /** the URL to load. */
 
87
    protected URL m_URL;
 
88
    
 
89
    /** the loader.*/
 
90
    protected Loader m_Loader;
 
91
    
 
92
    /** whether the loader is incremental. */
 
93
    protected boolean m_Incremental;
 
94
    
 
95
    /** the instance counter for the batch case. */
 
96
    protected int m_BatchCounter;
 
97
 
 
98
    /** the last internally read instance. */
 
99
    protected Instance m_IncrementalBuffer;
 
100
    
 
101
    /** the batch buffer. */
 
102
    protected Instances m_BatchBuffer;
 
103
    
 
104
    /**
 
105
     * Tries to load the data from the file. Can be either a regular file or
 
106
     * a web location (http://, https://, ftp:// or file://).
 
107
     * 
 
108
     * @param location          the name of the file to load
 
109
     * @throws Exception        if initialization fails
 
110
     */
 
111
    public DataSource(String location) throws Exception {
 
112
      super();
 
113
      
 
114
      // file or URL?
 
115
      if (    location.startsWith("http://")
 
116
           || location.startsWith("https://")
 
117
           || location.startsWith("ftp://")
 
118
           || location.startsWith("file://") )
 
119
        m_URL = new URL(location);
 
120
      else
 
121
        m_File = new File(location);
 
122
      
 
123
      // quick check: is it ARFF?
 
124
      if (isArff(location)) {
 
125
        m_Loader = new ArffLoader();
 
126
      }
 
127
      else {
 
128
        if (m_File != null)
 
129
          m_Loader = ConverterUtils.getLoaderForFile(location);
 
130
        else
 
131
          m_Loader = ConverterUtils.getURLLoaderForFile(location);
 
132
        
 
133
        // do we have a converter?
 
134
        if (m_Loader == null)
 
135
          throw new IllegalArgumentException("No suitable converter found for '" + location + "'!");
 
136
      }
 
137
      
 
138
      // incremental loader?
 
139
      m_Incremental = (m_Loader instanceof IncrementalConverter);
 
140
      
 
141
      reset();
 
142
    }
 
143
    
 
144
    /**
 
145
     * Initializes the datasource with the given dataset.
 
146
     * 
 
147
     * @param inst              the dataset to use
 
148
     */
 
149
    public DataSource(Instances inst) {
 
150
      super();
 
151
      
 
152
      m_BatchBuffer = inst;
 
153
      m_Loader      = null;
 
154
      m_File        = null;
 
155
      m_URL         = null;
 
156
      m_Incremental = false;
 
157
    }
 
158
    
 
159
    /**
 
160
     * Initializes the datasource with the given Loader.
 
161
     * 
 
162
     * @param loader            the Loader to use
 
163
     */
 
164
    public DataSource(Loader loader) {
 
165
      super();
 
166
 
 
167
      m_BatchBuffer = null;
 
168
      m_Loader      = loader;
 
169
      m_File        = null;
 
170
      m_URL         = null;
 
171
      m_Incremental = (m_Loader instanceof IncrementalConverter);
 
172
      
 
173
      initBatchBuffer();
 
174
    }
 
175
 
 
176
    /**
 
177
     * Initializes the datasource with the given input stream. This stream
 
178
     * is always interpreted as ARFF.
 
179
     * 
 
180
     * @param stream            the stream to use
 
181
     */
 
182
    public DataSource(InputStream stream) {
 
183
      super();
 
184
      
 
185
      m_BatchBuffer = null;
 
186
      m_Loader      = new ArffLoader();
 
187
      try {
 
188
        m_Loader.setSource(stream);
 
189
      }
 
190
      catch (Exception e) {
 
191
        m_Loader = null;
 
192
      }
 
193
      m_File        = null;
 
194
      m_URL         = null;
 
195
      m_Incremental = (m_Loader instanceof IncrementalConverter);
 
196
      
 
197
      initBatchBuffer();
 
198
    }
 
199
 
 
200
    /**
 
201
     * initializes the batch buffer if necessary, i.e., for non-incremental
 
202
     * loaders.
 
203
     */
 
204
    protected void initBatchBuffer() {
 
205
      try {
 
206
        if (!isIncremental())
 
207
          m_BatchBuffer = m_Loader.getDataSet();
 
208
        else
 
209
          m_BatchBuffer = null;
 
210
      }
 
211
      catch (Exception e) {
 
212
        e.printStackTrace();
 
213
      }
 
214
    }
 
215
    
 
216
    /**
 
217
     * returns whether the extension of the location is likely to be of ARFF
 
218
     * format, i.e., ending in ".arff" or ".arff.gz" (case-insensitive).
 
219
     * 
 
220
     * @param location          the file location to check
 
221
     * @return                  true if the location seems to be of ARFF format
 
222
     */
 
223
    public static boolean isArff(String location) {
 
224
      if (    location.toLowerCase().endsWith(ArffLoader.FILE_EXTENSION.toLowerCase())
 
225
           || location.toLowerCase().endsWith(ArffLoader.FILE_EXTENSION_COMPRESSED.toLowerCase()) )
 
226
        return true;
 
227
      else
 
228
        return false;
 
229
    }
 
230
    
 
231
    /**
 
232
     * returns whether the loader is an incremental one.
 
233
     * 
 
234
     * @return          true if the loader is a true incremental one
 
235
     */
 
236
    public boolean isIncremental() {
 
237
      return m_Incremental;
 
238
    }
 
239
    
 
240
    /**
 
241
     * returns the determined loader, null if the DataSource was initialized
 
242
     * with data alone and not a file/URL.
 
243
     * 
 
244
     * @return          the loader used for retrieving the data
 
245
     */
 
246
    public Loader getLoader() {
 
247
      return m_Loader;
 
248
    }
 
249
    
 
250
    /**
 
251
     * returns the full dataset, can be null in case of an error.
 
252
     * 
 
253
     * @return                  the full dataset
 
254
     * @throws Exception        if resetting of loader fails
 
255
     */
 
256
    public Instances getDataSet() throws Exception {
 
257
      Instances         result;
 
258
      
 
259
      result = null;
 
260
      
 
261
      // reset the loader
 
262
      reset();
 
263
      
 
264
      try {
 
265
        if (m_Loader != null)
 
266
          result = m_Loader.getDataSet();
 
267
        else
 
268
          result = m_BatchBuffer;
 
269
      }
 
270
      catch (Exception e) {
 
271
        e.printStackTrace();
 
272
        result = null;
 
273
      }
 
274
 
 
275
      return result;
 
276
    }
 
277
    
 
278
    /**
 
279
     * returns the full dataset with the specified class index set, 
 
280
     * can be null in case of an error.
 
281
     * 
 
282
     * @param classIndex        the class index for the dataset
 
283
     * @return                  the full dataset
 
284
     * @throws Exception        if resetting of loader fails
 
285
     */
 
286
    public Instances getDataSet(int classIndex) throws Exception {
 
287
      Instances         result;
 
288
      
 
289
      result = getDataSet();
 
290
      if (result != null)
 
291
        result.setClassIndex(classIndex);
 
292
      
 
293
      return result;
 
294
    }
 
295
    
 
296
    /**
 
297
     * resets the loader.
 
298
     * 
 
299
     * @throws Exception        if resetting fails
 
300
     */
 
301
    public void reset() throws Exception {
 
302
      if (m_File != null)
 
303
        ((AbstractFileLoader) m_Loader).setFile(m_File);
 
304
      else if (m_URL != null)
 
305
        ((URLSourcedLoader) m_Loader).setURL(m_URL.toString());
 
306
      else if (m_Loader != null)
 
307
        m_Loader.reset();
 
308
      
 
309
      m_BatchCounter      = 0;
 
310
      m_IncrementalBuffer = null;
 
311
 
 
312
      if (m_Loader != null) {
 
313
        if (!isIncremental())
 
314
          m_BatchBuffer = m_Loader.getDataSet();
 
315
        else
 
316
          m_BatchBuffer = null;
 
317
      }
 
318
    }
 
319
 
 
320
    /**
 
321
     * returns the structure of the data.
 
322
     * 
 
323
     * @return                  the structure of the data
 
324
     * @throws Exception        if something goes wrong
 
325
     */
 
326
    public Instances getStructure() throws Exception {
 
327
      if (m_Loader != null)
 
328
        return m_Loader.getStructure();
 
329
      else
 
330
        return new Instances(m_BatchBuffer, 0);
 
331
    }
 
332
 
 
333
    /**
 
334
     * returns the structure of the data, with the defined class index.
 
335
     * 
 
336
     * @param classIndex        the class index for the dataset
 
337
     * @return                  the structure of the data
 
338
     * @throws Exception        if something goes wrong
 
339
     */
 
340
    public Instances getStructure(int classIndex) throws Exception {
 
341
      Instances         result;
 
342
      
 
343
      result = getStructure();
 
344
      if (result != null)
 
345
        result.setClassIndex(classIndex);
 
346
      
 
347
      return result;
 
348
    }
 
349
    
 
350
    /**
 
351
     * returns whether there are more Instance objects in the data.
 
352
     * 
 
353
     * @param structure the structure of the dataset
 
354
     * @return          true if there are more Instance objects 
 
355
     *                  available
 
356
     * @see             #nextElement(Instances)
 
357
     */
 
358
    public boolean hasMoreElements(Instances structure) {
 
359
      boolean   result;
 
360
      
 
361
      result = false;
 
362
      
 
363
      if (isIncremental()) {
 
364
        // user still hasn't collected the last one?
 
365
        if (m_IncrementalBuffer != null) {
 
366
          result = true;
 
367
        }
 
368
        else {
 
369
          try {
 
370
            m_IncrementalBuffer = m_Loader.getNextInstance(structure);
 
371
            result              = (m_IncrementalBuffer != null);
 
372
          }
 
373
          catch (Exception e) {
 
374
            e.printStackTrace();
 
375
            result = false;
 
376
          }
 
377
        }
 
378
      }
 
379
      else {
 
380
        result = (m_BatchCounter < m_BatchBuffer.numInstances());
 
381
      }
 
382
      
 
383
      return result;
 
384
    }
 
385
    
 
386
    /**
 
387
     * returns the next element and sets the specified dataset, null if 
 
388
     * none available.
 
389
     * 
 
390
     * @param dataset   the dataset to set for the instance
 
391
     * @return          the next Instance
 
392
     */
 
393
    public Instance nextElement(Instances dataset) {
 
394
      Instance  result;
 
395
      
 
396
      result = null;
 
397
      
 
398
      if (isIncremental()) {
 
399
        // is there still an instance in the buffer?
 
400
        if (m_IncrementalBuffer != null) {
 
401
          result              = m_IncrementalBuffer;
 
402
          m_IncrementalBuffer = null;
 
403
        }
 
404
        else {
 
405
          try {
 
406
            result = m_Loader.getNextInstance(dataset);
 
407
          }
 
408
          catch (Exception e) {
 
409
            e.printStackTrace();
 
410
            result = null;
 
411
          }
 
412
        }
 
413
      }
 
414
      else {
 
415
        if (m_BatchCounter < m_BatchBuffer.numInstances()) {
 
416
          result = m_BatchBuffer.instance(m_BatchCounter);
 
417
          m_BatchCounter++;
 
418
        }
 
419
      }
 
420
 
 
421
      result.setDataset(dataset);
 
422
      
 
423
      return result;
 
424
    }
 
425
    
 
426
    /**
 
427
     * convencience method for loading a dataset in batch mode.
 
428
     * 
 
429
     * @param location          the dataset to load
 
430
     * @return                  the dataset
 
431
     * @throws Exception        if loading fails
 
432
     * @see                     #DataSource(String)
 
433
     */
 
434
    public static Instances read(String location) throws Exception {
 
435
      DataSource        source;
 
436
      Instances         result;
 
437
      
 
438
      source = new DataSource(location);
 
439
      result = source.getDataSet();
 
440
      
 
441
      return result;
 
442
    }
 
443
    
 
444
    /**
 
445
     * convencience method for loading a dataset in batch mode from a stream.
 
446
     * 
 
447
     * @param stream            the stream to load the dataset from
 
448
     * @return                  the dataset
 
449
     * @throws Exception        if loading fails
 
450
     * @see                     #DataSource(InputStream)
 
451
     */
 
452
    public static Instances read(InputStream stream) throws Exception {
 
453
      DataSource        source;
 
454
      Instances         result;
 
455
      
 
456
      source = new DataSource(stream);
 
457
      result = source.getDataSet();
 
458
      
 
459
      return result;
 
460
    }
 
461
    
 
462
    /**
 
463
     * convencience method for loading a dataset in batch mode.
 
464
     * 
 
465
     * @param loader            the loader to get the dataset from
 
466
     * @return                  the dataset
 
467
     * @throws Exception        if loading fails
 
468
     * @see                     #DataSource(Loader)
 
469
     */
 
470
    public static Instances read(Loader loader) throws Exception {
 
471
      DataSource        source;
 
472
      Instances         result;
 
473
      
 
474
      source = new DataSource(loader);
 
475
      result = source.getDataSet();
 
476
      
 
477
      return result;
 
478
    }
 
479
    
 
480
    /**
 
481
     * for testing only - takes a data file as input.
 
482
     * 
 
483
     * @param args              the commandline arguments
 
484
     * @throws Exception        if something goes wrong
 
485
     */
 
486
    public static void main(String[] args) throws Exception {
 
487
      if (args.length != 1) {
 
488
        System.out.println("\nUsage: " + DataSource.class.getName() + " <file>\n");
 
489
        System.exit(1);
 
490
      }
 
491
      
 
492
      DataSource loader = new DataSource(args[0]);
 
493
      
 
494
      System.out.println("Incremental? " + loader.isIncremental());
 
495
      System.out.println("Loader: " + loader.getLoader().getClass().getName());
 
496
      System.out.println("Data:\n");
 
497
      Instances structure = loader.getStructure();
 
498
      System.out.println(structure);
 
499
      while (loader.hasMoreElements(structure))
 
500
        System.out.println(loader.nextElement(structure));
 
501
      
 
502
      Instances inst = loader.getDataSet();
 
503
      loader = new DataSource(inst);
 
504
      System.out.println("\n\nProxy-Data:\n");
 
505
      System.out.println(loader.getStructure());
 
506
      while (loader.hasMoreElements(structure))
 
507
        System.out.println(loader.nextElement(inst));
 
508
    }
 
509
  }
 
510
 
 
511
  /**
 
512
   * Helper class for saving data to files. Via the ConverterUtils
 
513
   * class it determines which converter to use for saving the data.
 
514
   * It is the logical counterpart to <code>DataSource</code>.
 
515
   * 
 
516
   * @author FracPete (fracpete at waikato dot ac dot nz)
 
517
   * @version $Revision: 1.14 $
 
518
   * @see DataSource
 
519
   */
 
520
  public static class DataSink
 
521
    implements Serializable {
 
522
 
 
523
    /** for serialization. */
 
524
    private static final long serialVersionUID = -1504966891136411204L;
 
525
 
 
526
    /** the saver to use for storing the data. */
 
527
    protected Saver m_Saver = null;
 
528
    
 
529
    /** the stream to store the data in (always in ARFF format). */
 
530
    protected OutputStream m_Stream = null;
 
531
    
 
532
    /**
 
533
     * initializes the sink to save the data to the given file.
 
534
     * 
 
535
     * @param filename          the file to save data to
 
536
     * @throws Exception        if set of saver fails
 
537
     */
 
538
    public DataSink(String filename) throws Exception {
 
539
      m_Stream = null;
 
540
      
 
541
      if (DataSource.isArff(filename))
 
542
        m_Saver = new ArffSaver();
 
543
      else
 
544
        m_Saver = getSaverForFile(filename);
 
545
      
 
546
      ((AbstractFileSaver) m_Saver).setFile(new File(filename));
 
547
    }
 
548
    
 
549
    /**
 
550
     * initializes the sink to save the data to the given Saver (expected to be 
 
551
     * fully configured).
 
552
     * 
 
553
     * @param saver     the saver to use for saving the data
 
554
     */
 
555
    public DataSink(Saver saver) {
 
556
      m_Saver  = saver;
 
557
      m_Stream = null;
 
558
    }
 
559
    
 
560
    /**
 
561
     * initializes the sink to save the data in the stream (always in ARFF 
 
562
     * format).
 
563
     * 
 
564
     * @param stream    the output stream to use for storing the data in ARFF
 
565
     *                  format
 
566
     */
 
567
    public DataSink(OutputStream stream) {
 
568
      m_Saver  = null;
 
569
      m_Stream = stream;
 
570
    }
 
571
 
 
572
    /**
 
573
     * writes the given data either via the saver or to the defined 
 
574
     * output stream (depending on the constructor). In case of the stream, 
 
575
     * the stream is only flushed, but not closed.
 
576
     * 
 
577
     * @param data              the data to save
 
578
     * @throws Exception        if saving fails
 
579
     */
 
580
    public void write(Instances data) throws Exception {
 
581
      if (m_Saver != null) {
 
582
        m_Saver.setInstances(data);
 
583
        m_Saver.writeBatch();
 
584
      }
 
585
      else {
 
586
        m_Stream.write(data.toString().getBytes());
 
587
        m_Stream.flush();
 
588
      }
 
589
    }
 
590
    
 
591
    /**
 
592
     * writes the data to the given file.
 
593
     * 
 
594
     * @param filename          the file to write the data to
 
595
     * @param data              the data to store
 
596
     * @throws Exception        if writing fails
 
597
     */
 
598
    public static void write(String filename, Instances data) throws Exception {
 
599
      DataSink  sink;
 
600
      
 
601
      sink = new DataSink(filename);
 
602
      sink.write(data);
 
603
    }
 
604
    
 
605
    /**
 
606
     * writes the data via the given saver.
 
607
     * 
 
608
     * @param saver             the saver to use for writing the data
 
609
     * @param data              the data to store
 
610
     * @throws Exception        if writing fails
 
611
     */
 
612
    public static void write(Saver saver, Instances data) throws Exception {
 
613
      DataSink  sink;
 
614
      
 
615
      sink = new DataSink(saver);
 
616
      sink.write(data);
 
617
    }
 
618
    
 
619
    /**
 
620
     * writes the data to the given stream (always in ARFF format).
 
621
     * 
 
622
     * @param stream            the stream to write the data to (ARFF format)
 
623
     * @param data              the data to store
 
624
     * @throws Exception        if writing fails
 
625
     */
 
626
    public static void write(OutputStream stream, Instances data) throws Exception {
 
627
      DataSink  sink;
 
628
      
 
629
      sink = new DataSink(stream);
 
630
      sink.write(data);
 
631
    }
 
632
    
 
633
    /**
 
634
     * for testing only - takes a data file as input and a data file for the
 
635
     * output.
 
636
     * 
 
637
     * @param args              the commandline arguments
 
638
     * @throws Exception        if something goes wrong
 
639
     */
 
640
    public static void main(String[] args) throws Exception {
 
641
      if (args.length != 2) {
 
642
        System.out.println(
 
643
            "\nUsage: " + DataSource.class.getName() + " <input-file> <output-file>\n");
 
644
        System.exit(1);
 
645
      }
 
646
 
 
647
      // load data
 
648
      Instances data = DataSource.read(args[0]);
 
649
      
 
650
      // save data
 
651
      DataSink.write(args[1], data);
 
652
    }
 
653
  }
 
654
  
 
655
  /** the core loaders - hardcoded list necessary for RMI/Remote Experiments 
 
656
   * (comma-separated list). */
 
657
  public final static String CORE_FILE_LOADERS = 
 
658
      weka.core.converters.ArffLoader.class.getName() + ","
 
659
    + weka.core.converters.C45Loader.class.getName() + ","
 
660
    + weka.core.converters.CSVLoader.class.getName() + ","
 
661
    + weka.core.converters.DatabaseConverter.class.getName() + ","
 
662
    + weka.core.converters.LibSVMLoader.class.getName() + ","
 
663
    + weka.core.converters.SerializedInstancesLoader.class.getName() + ","
 
664
    + weka.core.converters.TextDirectoryLoader.class.getName() + ","
 
665
    + weka.core.converters.XRFFLoader.class.getName();
 
666
 
 
667
  /** the core savers - hardcoded list necessary for RMI/Remote Experiments 
 
668
   * (comma-separated list). */
 
669
  public final static String CORE_FILE_SAVERS =
 
670
      weka.core.converters.ArffSaver.class.getName() + ","
 
671
    + weka.core.converters.C45Saver.class.getName() + ","
 
672
    + weka.core.converters.CSVSaver.class.getName() + ","
 
673
    + weka.core.converters.DatabaseConverter.class.getName() + ","
 
674
    + weka.core.converters.LibSVMSaver.class.getName() + ","
 
675
    + weka.core.converters.SerializedInstancesSaver.class.getName() + ","
 
676
    + weka.core.converters.XRFFSaver.class.getName();
 
677
  
 
678
  /** all available loaders (extension &lt;-&gt; classname). */
 
679
  protected static Hashtable<String,String> m_FileLoaders;
 
680
  
 
681
  /** all available URL loaders (extension &lt;-&gt; classname). */
 
682
  protected static Hashtable<String,String> m_URLFileLoaders;
 
683
 
 
684
  /** all available savers (extension &lt;-&gt; classname). */
 
685
  protected static Hashtable<String,String> m_FileSavers;
 
686
  
 
687
  // determine all loaders/savers
 
688
  static {
 
689
    Vector classnames;
 
690
    
 
691
    try {
 
692
      // generate properties 
 
693
      // Note: does NOT work with RMI, hence m_FileLoadersCore/m_FileSaversCore
 
694
      GenericPropertiesCreator creator = new GenericPropertiesCreator();
 
695
      creator.execute(false);
 
696
      Properties props = creator.getOutputProperties();
 
697
 
 
698
      // init
 
699
      m_FileLoaders    = new Hashtable<String,String>();
 
700
      m_URLFileLoaders = new Hashtable<String,String>();
 
701
      m_FileSavers     = new Hashtable<String,String>();
 
702
      
 
703
      // loaders
 
704
      m_FileLoaders = getFileConverters(
 
705
                        props.getProperty(Loader.class.getName(), CORE_FILE_LOADERS),
 
706
                        new String[]{FileSourcedConverter.class.getName()});
 
707
      
 
708
      // URL loaders
 
709
      m_URLFileLoaders = getFileConverters(
 
710
                           props.getProperty(Loader.class.getName(), CORE_FILE_LOADERS),
 
711
                           new String[]{
 
712
                             FileSourcedConverter.class.getName(), 
 
713
                             URLSourcedLoader.class.getName()});
 
714
 
 
715
      // savers
 
716
      m_FileSavers = getFileConverters(
 
717
                        props.getProperty(Saver.class.getName(), CORE_FILE_SAVERS),
 
718
                        new String[]{FileSourcedConverter.class.getName()});
 
719
    }
 
720
    catch (Exception e) {
 
721
      // ignore
 
722
    }
 
723
    finally {
 
724
      // loaders
 
725
      if (m_FileLoaders.size() == 0) {
 
726
        classnames = GenericObjectEditor.getClassnames(AbstractFileLoader.class.getName());
 
727
        if (classnames.size() > 0)
 
728
          m_FileLoaders = getFileConverters(
 
729
                            classnames,
 
730
                            new String[]{FileSourcedConverter.class.getName()});
 
731
        else
 
732
          m_FileLoaders = getFileConverters(
 
733
                            CORE_FILE_LOADERS,
 
734
                            new String[]{FileSourcedConverter.class.getName()});
 
735
      }
 
736
 
 
737
      // URL loaders
 
738
      if (m_URLFileLoaders.size() == 0) {
 
739
        classnames = GenericObjectEditor.getClassnames(AbstractFileLoader.class.getName());
 
740
        if (classnames.size() > 0)
 
741
          m_URLFileLoaders = getFileConverters(
 
742
                               classnames,
 
743
                               new String[]{
 
744
                                   FileSourcedConverter.class.getName(), 
 
745
                                   URLSourcedLoader.class.getName()});
 
746
        else
 
747
          m_URLFileLoaders = getFileConverters(
 
748
                               CORE_FILE_LOADERS,
 
749
                               new String[]{
 
750
                                   FileSourcedConverter.class.getName(), 
 
751
                                   URLSourcedLoader.class.getName()});
 
752
      }
 
753
 
 
754
      // savers
 
755
      if (m_FileSavers.size() == 0) {
 
756
        classnames = GenericObjectEditor.getClassnames(AbstractFileSaver.class.getName());
 
757
        if (classnames.size() > 0)
 
758
          m_FileSavers = getFileConverters(
 
759
                           classnames,
 
760
                           new String[]{FileSourcedConverter.class.getName()});
 
761
        else
 
762
          m_FileSavers = getFileConverters(
 
763
                           CORE_FILE_SAVERS,
 
764
                           new String[]{FileSourcedConverter.class.getName()});
 
765
      }
 
766
    }
 
767
  }
 
768
  
 
769
  /**
 
770
   * returns a hashtable with the association 
 
771
   * "file extension &lt;-&gt; converter classname" for the comma-separated list
 
772
   * of converter classnames.
 
773
   * 
 
774
   * @param classnames  comma-separated list of converter classnames
 
775
   * @param intf        interfaces the converters have to implement
 
776
   * @return            hashtable with ExtensionFileFilters
 
777
   */
 
778
  protected static Hashtable<String,String> getFileConverters(String classnames, String[] intf) {
 
779
    Vector      list;
 
780
    String[]    names;
 
781
    int         i;
 
782
    
 
783
    list  = new Vector();
 
784
    names = classnames.split(",");
 
785
    for (i = 0; i < names.length; i++)
 
786
      list.add(names[i]);
 
787
    
 
788
    return getFileConverters(list, intf);
 
789
  }
 
790
  
 
791
  /**
 
792
   * returns a hashtable with the association 
 
793
   * "file extension &lt;-&gt; converter classname" for the list of converter 
 
794
   * classnames.
 
795
   * 
 
796
   * @param classnames  list of converter classnames
 
797
   * @param intf        interfaces the converters have to implement
 
798
   * @return            hashtable with ExtensionFileFilters
 
799
   */
 
800
  protected static Hashtable<String,String> getFileConverters(Vector classnames, String[] intf) {
 
801
    Hashtable<String,String>    result;
 
802
    String                      classname;
 
803
    Class                       cls;
 
804
    String[]                    ext;
 
805
    FileSourcedConverter        converter;
 
806
    int                         i;
 
807
    int                         n;
 
808
    
 
809
    result = new Hashtable<String,String>();
 
810
    
 
811
    for (i = 0; i < classnames.size(); i++) {
 
812
      classname = (String) classnames.get(i);
 
813
 
 
814
      // all necessary interfaces implemented?
 
815
      for (n = 0; n < intf.length; n++) {
 
816
        if (!ClassDiscovery.hasInterface(intf[n], classname))
 
817
          continue;
 
818
      }
 
819
      
 
820
      // get data from converter
 
821
      try {
 
822
        cls       = Class.forName(classname);
 
823
        converter = (FileSourcedConverter) cls.newInstance();
 
824
        ext       = converter.getFileExtensions();
 
825
      }
 
826
      catch (Exception e) {
 
827
        cls       = null;
 
828
        converter = null;
 
829
        ext       = new String[0];
 
830
      }
 
831
      
 
832
      if (converter == null)
 
833
        continue;
 
834
 
 
835
      for (n = 0; n < ext.length; n++)
 
836
        result.put(ext[n], classname);
 
837
    }
 
838
    
 
839
    return result;
 
840
  }
 
841
 
 
842
  /**
 
843
   * Gets token, skipping empty lines.
 
844
   *
 
845
   * @param tokenizer           the stream tokenizer
 
846
   * @throws IOException        if reading the next token fails
 
847
   */
 
848
  public static void getFirstToken(StreamTokenizer tokenizer) 
 
849
    throws IOException {
 
850
    
 
851
    while (tokenizer.nextToken() == StreamTokenizer.TT_EOL){};
 
852
    if ((tokenizer.ttype == '\'') ||
 
853
        (tokenizer.ttype == '"')) {
 
854
      tokenizer.ttype = StreamTokenizer.TT_WORD;
 
855
    } else if ((tokenizer.ttype == StreamTokenizer.TT_WORD) &&
 
856
               (tokenizer.sval.equals("?"))) {
 
857
      tokenizer.ttype = '?';
 
858
    }
 
859
  }
 
860
 
 
861
  /**
 
862
   * Gets token.
 
863
   *
 
864
   * @param tokenizer           the stream tokenizer
 
865
   * @throws IOException        if reading the next token fails
 
866
   */
 
867
  public static void getToken(StreamTokenizer tokenizer) throws IOException {
 
868
    
 
869
    tokenizer.nextToken();
 
870
    if (tokenizer.ttype== StreamTokenizer.TT_EOL) {
 
871
      return;
 
872
    }
 
873
 
 
874
    if ((tokenizer.ttype == '\'') ||
 
875
        (tokenizer.ttype == '"')) {
 
876
      tokenizer.ttype = StreamTokenizer.TT_WORD;
 
877
    } else if ((tokenizer.ttype == StreamTokenizer.TT_WORD) &&
 
878
               (tokenizer.sval.equals("?"))) {
 
879
      tokenizer.ttype = '?';
 
880
    }
 
881
  }
 
882
 
 
883
  /**
 
884
   * Throws error message with line number and last token read.
 
885
   *
 
886
   * @param theMsg              the error message to be thrown
 
887
   * @param tokenizer           the stream tokenizer
 
888
   * @throws IOException        containing the error message
 
889
   */
 
890
  public static void errms(StreamTokenizer tokenizer, String theMsg) 
 
891
    throws IOException {
 
892
    
 
893
    throw new IOException(theMsg + ", read " + tokenizer.toString());
 
894
  }
 
895
 
 
896
  /**
 
897
   * returns a vector with the classnames of all the loaders from the 
 
898
   * given hashtable.
 
899
   * 
 
900
   * @param ht          the hashtable with the extension/converter relation
 
901
   * @return            the classnames of the loaders
 
902
   */
 
903
  protected static Vector<String> getConverters(Hashtable<String,String> ht) {
 
904
    Vector<String>      result;
 
905
    Enumeration<String> enm;
 
906
    String              converter;
 
907
    
 
908
    result = new Vector<String>();
 
909
    
 
910
    // get all classnames
 
911
    enm = ht.elements();
 
912
    while (enm.hasMoreElements()) {
 
913
      converter = enm.nextElement();
 
914
      if (!result.contains(converter))
 
915
        result.add(converter);
 
916
    }
 
917
    
 
918
    // sort names
 
919
    Collections.sort(result);
 
920
    
 
921
    return result;
 
922
  }
 
923
  
 
924
  /**
 
925
   * tries to determine the converter to use for this kind of file, returns
 
926
   * null if none can be found in the given hashtable.
 
927
   * 
 
928
   * @param filename    the file to return a converter for
 
929
   * @param ht          the hashtable with the relation extension/converter
 
930
   * @return            the converter if one was found, null otherwise
 
931
   */
 
932
  protected static Object getConverterForFile(String filename, Hashtable<String,String> ht) {
 
933
    Object      result;
 
934
    String      extension;
 
935
    int         index;
 
936
    
 
937
    result = null;
 
938
    
 
939
    index = filename.lastIndexOf('.');
 
940
    if (index > -1) {
 
941
      extension = filename.substring(index).toLowerCase();
 
942
      result    = getConverterForExtension(extension, ht);
 
943
      // is it a compressed format?
 
944
      if (extension.equals(".gz") && result == null) {
 
945
        index     = filename.lastIndexOf('.', index - 1);
 
946
        extension = filename.substring(index).toLowerCase();
 
947
        result    = getConverterForExtension(extension, ht);
 
948
      }
 
949
    }
 
950
    
 
951
    return result;
 
952
  }
 
953
 
 
954
  /**
 
955
   * tries to determine the loader to use for this kind of extension, returns
 
956
   * null if none can be found.
 
957
   * 
 
958
   * @param extension   the file extension to return a converter for
 
959
   * @param ht          the hashtable with the relation extension/converter
 
960
   * @return            the converter if one was found, null otherwise
 
961
   */
 
962
  protected static Object getConverterForExtension(String extension, Hashtable<String,String> ht) {
 
963
    Object      result;
 
964
    String      classname;
 
965
    
 
966
    result    = null;
 
967
    classname = (String) ht.get(extension);
 
968
    if (classname != null) {
 
969
      try {
 
970
        result = Class.forName(classname).newInstance();
 
971
      }
 
972
      catch (Exception e) {
 
973
        result = null;
 
974
        e.printStackTrace();
 
975
      }
 
976
    }
 
977
    
 
978
    return result;
 
979
  }
 
980
  
 
981
  /**
 
982
   * checks whether the given class is one of the hardcoded core file loaders.
 
983
   * 
 
984
   * @param classname   the class to check
 
985
   * @return            true if the class is one of the core loaders
 
986
   * @see               #CORE_FILE_LOADERS
 
987
   */
 
988
  public static boolean isCoreFileLoader(String classname) {
 
989
    boolean     result;
 
990
    String[]    classnames;
 
991
    
 
992
    classnames = CORE_FILE_LOADERS.split(",");
 
993
    result     = (Arrays.binarySearch(classnames, classname) >= 0);
 
994
    
 
995
    return result;
 
996
  }
 
997
  
 
998
  /**
 
999
   * returns a vector with the classnames of all the file loaders.
 
1000
   * 
 
1001
   * @return            the classnames of the loaders
 
1002
   */
 
1003
  public static Vector<String> getFileLoaders() {
 
1004
    return getConverters(m_FileLoaders);
 
1005
  }
 
1006
  
 
1007
  /**
 
1008
   * tries to determine the loader to use for this kind of file, returns
 
1009
   * null if none can be found.
 
1010
   * 
 
1011
   * @param filename    the file to return a converter for
 
1012
   * @return            the converter if one was found, null otherwise
 
1013
   */
 
1014
  public static AbstractFileLoader getLoaderForFile(String filename) {
 
1015
    return (AbstractFileLoader) getConverterForFile(filename, m_FileLoaders);
 
1016
  }
 
1017
 
 
1018
  /**
 
1019
   * tries to determine the loader to use for this kind of file, returns
 
1020
   * null if none can be found.
 
1021
   * 
 
1022
   * @param file        the file to return a converter for
 
1023
   * @return            the converter if one was found, null otherwise
 
1024
   */
 
1025
  public static AbstractFileLoader getLoaderForFile(File file) {
 
1026
    return getLoaderForFile(file.getAbsolutePath());
 
1027
  }
 
1028
 
 
1029
  /**
 
1030
   * tries to determine the loader to use for this kind of extension, returns
 
1031
   * null if none can be found.
 
1032
   * 
 
1033
   * @param extension   the file extension to return a converter for
 
1034
   * @return            the converter if one was found, null otherwise
 
1035
   */
 
1036
  public static AbstractFileLoader getLoaderForExtension(String extension) {
 
1037
    return (AbstractFileLoader) getConverterForExtension(extension, m_FileLoaders);
 
1038
  }
 
1039
 
 
1040
  /**
 
1041
   * returns a vector with the classnames of all the URL file loaders.
 
1042
   * 
 
1043
   * @return            the classnames of the loaders
 
1044
   */
 
1045
  public static Vector<String> getURLFileLoaders() {
 
1046
    return getConverters(m_URLFileLoaders);
 
1047
  }
 
1048
  
 
1049
  /**
 
1050
   * tries to determine the URL loader to use for this kind of file, returns
 
1051
   * null if none can be found.
 
1052
   * 
 
1053
   * @param filename    the file to return a URL converter for
 
1054
   * @return            the converter if one was found, null otherwise
 
1055
   */
 
1056
  public static AbstractFileLoader getURLLoaderForFile(String filename) {
 
1057
    return (AbstractFileLoader) getConverterForFile(filename, m_URLFileLoaders);
 
1058
  }
 
1059
 
 
1060
  /**
 
1061
   * tries to determine the URL loader to use for this kind of file, returns
 
1062
   * null if none can be found.
 
1063
   * 
 
1064
   * @param file        the file to return a URL converter for
 
1065
   * @return            the converter if one was found, null otherwise
 
1066
   */
 
1067
  public static AbstractFileLoader getURLLoaderForFile(File file) {
 
1068
    return getURLLoaderForFile(file.getAbsolutePath());
 
1069
  }
 
1070
 
 
1071
  /**
 
1072
   * tries to determine the URL loader to use for this kind of extension, returns
 
1073
   * null if none can be found.
 
1074
   * 
 
1075
   * @param extension   the file extension to return a URL converter for
 
1076
   * @return            the converter if one was found, null otherwise
 
1077
   */
 
1078
  public static AbstractFileLoader getURLLoaderForExtension(String extension) {
 
1079
    return (AbstractFileLoader) getConverterForExtension(extension, m_URLFileLoaders);
 
1080
  }
 
1081
  
 
1082
  /**
 
1083
   * checks whether the given class is one of the hardcoded core file savers.
 
1084
   * 
 
1085
   * @param classname   the class to check
 
1086
   * @return            true if the class is one of the core savers
 
1087
   * @see               #CORE_FILE_SAVERS
 
1088
   */
 
1089
  public static boolean isCoreFileSaver(String classname) {
 
1090
    boolean     result;
 
1091
    String[]    classnames;
 
1092
    
 
1093
    classnames = CORE_FILE_SAVERS.split(",");
 
1094
    result     = (Arrays.binarySearch(classnames, classname) >= 0);
 
1095
    
 
1096
    return result;
 
1097
  }
 
1098
 
 
1099
  /**
 
1100
   * returns a vector with the classnames of all the file savers.
 
1101
   * 
 
1102
   * @return            the classnames of the savers
 
1103
   */
 
1104
  public static Vector<String> getFileSavers() {
 
1105
    return getConverters(m_FileSavers);
 
1106
  }
 
1107
 
 
1108
  /**
 
1109
   * tries to determine the saver to use for this kind of file, returns
 
1110
   * null if none can be found.
 
1111
   * 
 
1112
   * @param filename    the file to return a converter for
 
1113
   * @return            the converter if one was found, null otherwise
 
1114
   */
 
1115
  public static AbstractFileSaver getSaverForFile(String filename) {
 
1116
    return (AbstractFileSaver) getConverterForFile(filename, m_FileSavers);
 
1117
  }
 
1118
 
 
1119
  /**
 
1120
   * tries to determine the saver to use for this kind of file, returns
 
1121
   * null if none can be found.
 
1122
   * 
 
1123
   * @param file        the file to return a converter for
 
1124
   * @return            the converter if one was found, null otherwise
 
1125
   */
 
1126
  public static AbstractFileSaver getSaverForFile(File file) {
 
1127
    return getSaverForFile(file.getAbsolutePath());
 
1128
  }
 
1129
 
 
1130
  /**
 
1131
   * tries to determine the saver to use for this kind of extension, returns
 
1132
   * null if none can be found.
 
1133
   * 
 
1134
   * @param extension   the file extension to return a converter for
 
1135
   * @return            the converter if one was found, null otherwise
 
1136
   */
 
1137
  public static AbstractFileSaver getSaverForExtension(String extension) {
 
1138
    return (AbstractFileSaver) getConverterForExtension(extension, m_FileSavers);
 
1139
  }
 
1140
}