Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2004 University of New Mexico and the 
4
 *                  Regents of the University of California
5
 *
6
 *   '$Author: costa $'
7
 *     '$Date: 2004-08-04 12:42:38 -0700 (Wed, 04 Aug 2004) $'
8
 * '$Revision: 2237 $'
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
 */
24

    
25
package edu.ucsb.nceas.metacat.harvesterClient;
26

    
27
import java.awt.BorderLayout;
28
import java.awt.Dimension;
29
import java.awt.event.ActionEvent;
30
import java.awt.event.ActionListener;
31
import java.io.File;
32
import java.io.FileInputStream;
33
import java.io.FileWriter;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.InputStreamReader;
37
import java.io.PrintStream;
38
import java.io.PrintWriter;
39
import java.io.Reader;
40
import java.util.Properties;
41
import javax.swing.JButton;
42
import javax.swing.JFileChooser;
43
import javax.swing.JFrame;
44
import javax.swing.JMenu;
45
import javax.swing.JMenuBar;
46
import javax.swing.JMenuItem;
47
import javax.swing.JOptionPane;
48
import javax.swing.JPanel;
49
import javax.swing.JScrollPane;
50
import javax.swing.JTable;
51
import javax.swing.KeyStroke;
52
import javax.swing.ListSelectionModel;
53
import javax.swing.event.ListSelectionEvent;
54
import javax.swing.event.ListSelectionListener;
55
import javax.swing.table.AbstractTableModel;
56
import javax.swing.table.TableColumn;
57
import javax.swing.table.TableModel;
58
import javax.swing.text.Document;
59
import javax.xml.parsers.ParserConfigurationException;
60
import org.xml.sax.Attributes;
61
import org.xml.sax.ContentHandler;
62
import org.xml.sax.ErrorHandler;
63
import org.xml.sax.InputSource;
64
import org.xml.sax.SAXException;
65
import org.xml.sax.SAXParseException;
66
import org.xml.sax.XMLReader;
67
import org.xml.sax.helpers.DefaultHandler;
68
import org.xml.sax.helpers.XMLReaderFactory;
69

    
70
/**
71
 * The Harvest List Editor reads a Harvest List XML file and displays it as a 
72
 * JTable. Allows user to add, modify, or delete <Document> elements in the
73
 * Harvest List, and save changes back to the disk.
74
 * 
75
 */
76
public class HarvestListEditor extends JFrame implements ActionListener {
77

    
78
  String clipboardDocumentType = "";
79
  String clipboardDocumentURL = "";
80
  String clipboardIdentifier = "";
81
  String clipboardRevision = "";
82
  String clipboardScope = "";
83
	JButton copyButton;
84
	JButton cutButton;
85
  String defaultDocumentType = "eml://ecoinformatics.org/eml-2.0.0";
86
  String defaultDocumentURL = "http://";
87
  String defaultHarvestList = "";
88
  String defaultIdentifier = "1";
89
  String defaultRevision = "1";
90
  String defaultScope = "dataset";
91
	Document doc;
92
	JScrollPane docPane;
93
  JFileChooser fileChooser = new JFileChooser();
94
  File harvestListFile;
95
  boolean harvestListHasChanged = false;
96
	JMenuBar menuBar;
97
  final int numColumns = 6;
98
  final int numRows = 300;
99
	JButton pasteButton;
100
	JButton pasteDefaultsButton;
101
  Properties properties;
102
  private String schemaLocation = 
103
//    "eml://ecoinformatics.org/harvestList ../../lib/harvester/harvestList.xsd";
104
    "eml://ecoinformatics.org/harvestList harvestList.xsd";
105
  int selectedRow = -1;
106
  final JTable table;
107
  TableModel tableModel;
108
  File tempFile;
109
  String title = "Harvest List Editor";
110
  
111
  // Menu items
112
  JMenuItem exitMenuItem = new JMenuItem("Exit");
113
  JMenuItem newMenuItem = new JMenuItem("New");
114
  JMenuItem openMenuItem = new JMenuItem("Open...");
115
  JMenuItem saveMenuItem = new JMenuItem("Save");
116
  JMenuItem saveAsMenuItem = new JMenuItem("Save As...");
117
  JMenuItem validateMenuItem = new JMenuItem("Validate");
118
  
119

    
120
  /**
121
   * The main program. Instantiate the main frame, a HarvestListEditor object.
122
   * 
123
   * @param args
124
   */
125
  public static void main(String[] args) {
126
    HarvestListEditor harvestListEditor = new HarvestListEditor();
127
  }
128

    
129

    
130
  /**
131
   * Constructor for HarvestListEditor class.
132
   */
133
  public HarvestListEditor() {
134
		super("Harvest List Editor");
135

    
136
		JPanel buttonPanel = new JPanel();
137
    String[] fileItems = 
138
                  new String[] {"New", "Open...", "Save", "Save As...", "Exit"};
139
		JMenu fileMenu = new JMenu("File");
140
		char[] fileShortCuts = {'N', 'O', 'S', 'A', 'X'};
141
    TableColumn tableColumn;
142
    
143
    loadProperties();
144
		setSize(1000, 400);
145
		setDefaultCloseOperation(EXIT_ON_CLOSE);
146
		menuBar = new JMenuBar();
147
    
148
    /*
149
     * Add menu items to the File menu
150
     */
151
		newMenuItem.setAccelerator(KeyStroke.getKeyStroke('N',
152
									                                     java.awt.Event.CTRL_MASK,
153
                                                       false));
154
		newMenuItem.addActionListener(this);
155
		fileMenu.add(newMenuItem);
156

    
157
		openMenuItem.setAccelerator(KeyStroke.getKeyStroke('O',
158
									                                     java.awt.Event.CTRL_MASK,
159
                                                       false));
160
		openMenuItem.addActionListener(this);
161
		fileMenu.add(openMenuItem);
162

    
163
		saveMenuItem.setAccelerator(KeyStroke.getKeyStroke('S',
164
									                                     java.awt.Event.CTRL_MASK,
165
                                                       false));
166
		saveMenuItem.addActionListener(this);
167
    saveMenuItem.setEnabled(false);
168
		fileMenu.add(saveMenuItem);
169

    
170
		saveAsMenuItem.setAccelerator(KeyStroke.getKeyStroke('A',
171
									                                     java.awt.Event.CTRL_MASK,
172
                                                       false));
173
		saveAsMenuItem.addActionListener(this);
174
		fileMenu.add(saveAsMenuItem);
175

    
176
		validateMenuItem.setAccelerator(KeyStroke.getKeyStroke('V',
177
									                                     java.awt.Event.CTRL_MASK,
178
                                                       false));
179
		validateMenuItem.addActionListener(this);
180
		fileMenu.add(validateMenuItem);
181

    
182
		exitMenuItem.setAccelerator(KeyStroke.getKeyStroke('X',
183
									                                     java.awt.Event.CTRL_MASK,
184
                                                       false));
185
		exitMenuItem.addActionListener(this);
186
		fileMenu.add(exitMenuItem);
187
    
188
		menuBar.add(fileMenu);      // Add the File menu to the menu bar
189
		setJMenuBar(menuBar);       // Set the frame's menu bar to this menu bar
190

    
191
    //table = new JTable(numRows, numColumns);
192
    table = new JTable(new HarvestListTableModel());
193
    table.setPreferredScrollableViewportSize(new Dimension(900, 300));
194
    tableColumn = table.getColumnModel().getColumn(0);
195
    tableColumn.setPreferredWidth(30);
196
    tableColumn = table.getColumnModel().getColumn(1);
197
    tableColumn.setPreferredWidth(200);
198
    tableColumn = table.getColumnModel().getColumn(2);
199
    tableColumn.setPreferredWidth(50);
200
    tableColumn = table.getColumnModel().getColumn(3);
201
    tableColumn.setPreferredWidth(50);
202
    tableColumn = table.getColumnModel().getColumn(4);
203
    tableColumn.setPreferredWidth(250);
204
    tableColumn = table.getColumnModel().getColumn(5);
205
    tableColumn.setPreferredWidth(320);
206
    
207
    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
208
    tableModel = table.getModel();
209
    initHarvestList();
210

    
211
    //Ask to be notified of selection changes.
212
    ListSelectionModel rowSM = table.getSelectionModel();
213

    
214
    rowSM.addListSelectionListener(new ListSelectionListener() {
215
      public void valueChanged(ListSelectionEvent e) {
216
        ListSelectionModel lsm;
217

    
218
        //Ignore extra messages.
219
        if (e.getValueIsAdjusting()) return;
220

    
221
        lsm = (ListSelectionModel)e.getSource();
222

    
223
        // If now row is selected, disable all buttons.
224
        if (lsm.isSelectionEmpty()) {
225
          selectedRow = -1;
226
          cutButton.setEnabled(false);
227
          copyButton.setEnabled(false);
228
          pasteButton.setEnabled(false);
229
          pasteDefaultsButton.setEnabled(false);
230
        }
231
        // If a row is selected, manage the buttons based on the selected row
232
        // and the current clipboard values.
233
        else {
234
          selectedRow = lsm.getMinSelectionIndex();
235
          manageButtons(selectedRow);
236
        }
237
      }
238
    });
239

    
240
    docPane = new JScrollPane(table);
241
    getContentPane().add(docPane, BorderLayout.CENTER);
242

    
243
		cutButton = new JButton("Cut");
244
		copyButton = new JButton("Copy");
245
		pasteButton = new JButton("Paste");
246
		pasteDefaultsButton = new JButton("Paste Defaults");
247

    
248
    // Action listener for the Copy button.    
249
    copyButton.addActionListener(new ActionListener() {
250
      public void actionPerformed(ActionEvent ae) {				
251
        copyRow(selectedRow);
252
        manageButtons(selectedRow);
253
        harvestListHasChanged = true;
254
			}
255
		}
256
    );
257

    
258
    // Action listener for the Cut button.    
259
    cutButton.addActionListener(new ActionListener() {
260
      public void actionPerformed(ActionEvent ae) {				
261
        cutRow(selectedRow);
262
        manageButtons(selectedRow);
263
        harvestListHasChanged = true;
264
			}
265
		}
266
    );
267

    
268
    // Action listener for the Paste button.    
269
    pasteButton.addActionListener(new ActionListener() {
270
      public void actionPerformed(ActionEvent ae) {
271
        pasteRow(selectedRow);
272
        manageButtons(selectedRow);
273
        harvestListHasChanged = true;
274
			}
275
		}
276
    );
277

    
278
    // Action listener for the Paste Defaults button.    
279
    pasteDefaultsButton.addActionListener(new ActionListener() {
280
      public void actionPerformed(ActionEvent ae) {
281
        pasteDefaultValues(selectedRow);
282
        manageButtons(selectedRow);
283
        harvestListHasChanged = true;
284
			}
285
		}
286
    );
287

    
288
    cutButton.setEnabled(false);
289
    copyButton.setEnabled(false);
290
    pasteButton.setEnabled(false);
291
    pasteDefaultsButton.setEnabled(false);
292
		buttonPanel.add(cutButton);
293
		buttonPanel.add(copyButton);
294
		buttonPanel.add(pasteButton);
295
		buttonPanel.add(pasteDefaultsButton);
296
		buttonPanel.setOpaque(true);
297
		getContentPane().add(buttonPanel, BorderLayout.SOUTH);
298

    
299
    // If the default Harvest List option has a value, and the file exists, 
300
    // loads its contents.
301
    //
302
    if ((defaultHarvestList != null) && (!defaultHarvestList.equals(""))) {
303
      harvestListFile = new File(defaultHarvestList);
304
      if (harvestListFile.exists()) {
305
        try {
306
          loadHarvestList(harvestListFile);
307
          saveMenuItem.setEnabled(true);
308
        }
309
        catch (ParserConfigurationException e) {
310
          System.out.println("Error parsing Harvest List: " + e.getMessage());
311
        }        
312
      }
313
    }
314
    else {
315
      fileNew();
316
    }
317

    
318
    try {    
319
      tempFile = File.createTempFile("harvestListTemp", ".xml");
320
    }
321
    catch (IOException ioe) {
322
      System.out.println("Error creating temporary file: " + ioe.getMessage());
323
    }
324

    
325
		setVisible(true);
326
  }
327

    
328

    
329
  /**
330
   * Implements action listeners for menu items.
331
   * 
332
   * @param e   An ActionEvent object, determines the menu item that was
333
   *            selected.
334
   */
335
	public void actionPerformed(ActionEvent e) {
336
		if ((e.getActionCommand()).equals("New")) {
337
      fileNew();
338
		}    
339
		else if ((e.getActionCommand()).equals("Open...")) {
340
      fileOpen();
341
    }    
342
    else if ((e.getActionCommand()).equals("Save")) {
343
      fileSave();
344
    }    
345
		else if ((e.getActionCommand()).equals("Save As...")) {
346
      fileSaveAs();
347
    }
348
		else if ((e.getActionCommand()).equals("Validate")) {
349
      fileValidate();
350
    }
351
    else if ((e.getActionCommand()).equals("Exit")) {
352
      fileExit();
353
    }
354
	}
355
  
356

    
357
  /**
358
   * Adds a new row to the table, setting values for each of the five columns
359
   * in the row.
360
   * 
361
   * @param rowIndex              the row index
362
   * @param scope                 the scope string
363
   * @param identifier            the identifier string
364
   * @param revision              the revision string
365
   * @param documentType          the document type
366
   * @param documentURL           the document URL
367
   */
368
  void addRow(int rowIndex, String scope, String identifier, String revision,
369
              String documentType, String documentURL) {
370
    tableModel.setValueAt(scope,                 rowIndex, 1);
371
    tableModel.setValueAt(identifier,            rowIndex, 2);
372
    tableModel.setValueAt(revision,              rowIndex, 3);
373
    tableModel.setValueAt(documentType,          rowIndex, 4);
374
    tableModel.setValueAt(documentURL,           rowIndex, 5);
375
  }
376

    
377

    
378
  /**
379
   * Composes a single tag line to be written in the Harvest List.
380
   * 
381
   * @param indentLevel    the number of spaces to begin the line with
382
   * @param tag            the tag name
383
   * @param text           the text to insert within the begin and end tags
384
   * @return line          the composed line
385
   */
386
  String composeLine(int indentLevel, String tag, String text) {
387
    String line = "";
388
    
389
    for (int i = 0; i < indentLevel; i++) {
390
      line += " ";
391
    }
392
    
393
    line += "<" + tag + ">";
394
    line += text;
395
    line += "</" + tag + ">";
396
    
397
    return line;
398
  }
399
  
400

    
401
  /**
402
   * Clears all rows in the table, setting all values to null.
403
   */
404
  void clearHarvestList() {
405
    for (int rowIndex = 0; rowIndex < numRows; rowIndex++) {
406
      clearRow(rowIndex);
407
    }
408
  }
409
  
410

    
411
  /**
412
   * Clears a single row in the tables, setting all five fields to null.
413
   * 
414
   * @param rowIndex   the index to the table row to be cleared
415
   */
416
  void clearRow(int rowIndex) {
417
    final String nil = "";
418
    
419
    tableModel.setValueAt(nil, rowIndex, 1);
420
    tableModel.setValueAt(nil, rowIndex, 2);
421
    tableModel.setValueAt(nil, rowIndex, 3);
422
    tableModel.setValueAt(nil, rowIndex, 4);
423
    tableModel.setValueAt(nil, rowIndex, 5);    
424
  }
425
  
426

    
427
  /**
428
   * Copies the values in a given table row to the clipboard.
429
   * 
430
   * @param rowIndex  the index of the table row to be copied
431
   */
432
  void copyRow(int rowIndex) {
433
    clipboardScope = (String) tableModel.getValueAt(rowIndex, 1);
434
    clipboardIdentifier = (String) tableModel.getValueAt(rowIndex, 2);
435
    clipboardRevision = (String) tableModel.getValueAt(rowIndex, 3);
436
    clipboardDocumentType = (String) tableModel.getValueAt(rowIndex, 4);    
437
    clipboardDocumentURL = (String) tableModel.getValueAt(rowIndex, 5);
438
  }
439
  
440

    
441
  /**
442
   * Cuts a row from the table. The row is copied to the clipboard and then
443
   * cleared.
444
   * 
445
   * @param rowIndex  the index of the table row to be copied
446
   */
447
  void cutRow(int rowIndex) {
448
    copyRow(rowIndex);
449
    clearRow(rowIndex);
450
  }
451
  
452

    
453
  /**
454
   * Exit from the Harvest List Editor. Prompt to save changes if appropriate.
455
   */
456
  void fileExit() {
457
    int value;
458

    
459
    if (harvestListHasChanged) {
460
      value = saveChangesDialog();
461
      
462
      if (value == JOptionPane.YES_OPTION) {
463
        try {
464
          // Save the changes then exit
465
          //
466
          fileSave();
467
          System.exit(0);
468
        }
469
        catch (Exception exception) {
470
          exception.printStackTrace();
471
        }
472
      }
473
      else if (value == JOptionPane.NO_OPTION) {
474
        // Exit without saving changes
475
        System.exit(0);
476
      } 
477
    }
478
    else {
479
      System.exit(0);
480
    }
481
  }
482
  
483

    
484
  /**
485
   * Replace the current Harvest List with an empty Harvest List. Prompt to save
486
   * changes if appropriate.
487
   */
488
  void fileNew() {
489
    int value;
490
    
491
    if (harvestListHasChanged) {
492
      value = saveChangesDialog();
493
      
494
      if (value == JOptionPane.YES_OPTION) {
495
        try {
496
          fileSave();
497
        }
498
        catch (Exception exception) {
499
          exception.printStackTrace();
500
        }
501
      }
502
      else if (value == JOptionPane.CANCEL_OPTION) {
503
        return;
504
      }
505
    }
506

    
507
    clearHarvestList();
508
    harvestListFile = null;
509
    setTitle(title + ": (Untitled)");
510
    saveMenuItem.setEnabled(false);
511
    harvestListHasChanged = false;    
512
  }
513
  
514

    
515
  /**
516
   * Opens a file dialog to load a Harvest List. Prompts to save changes to the
517
   * current Harvest List if appropriate.
518
   */
519
  void fileOpen() {
520
    int value;
521
    
522
    if (harvestListHasChanged) {
523
      value = saveChangesDialog();
524
      
525
      if (value == JOptionPane.YES_OPTION) {
526
        try {
527
          fileSave();
528
        }
529
        catch (Exception exception) {
530
          exception.printStackTrace();
531
        }
532
      }
533
      else if (value == JOptionPane.CANCEL_OPTION) {
534
        return;
535
      }
536
    }
537

    
538
    value = fileChooser.showOpenDialog(HarvestListEditor.this);
539
    
540
    if (value == JFileChooser.APPROVE_OPTION) {
541
      harvestListFile = fileChooser.getSelectedFile();
542
      try {
543
        clearHarvestList();
544
        loadHarvestList(harvestListFile);
545
      }
546
      catch (ParserConfigurationException e) {
547
        System.out.println("Error parsing Harvest List: " + e.getMessage());
548
      }        
549
      harvestListHasChanged = false;
550
      saveMenuItem.setEnabled(true);
551
    }
552
  }
553
  
554

    
555
  /**
556
   * Save the current Harvest List to disk.
557
   */
558
  void fileSave() {
559
    if (harvestListFile != null) {
560
      writeFile(harvestListFile);
561
      harvestListHasChanged = false;
562
    }
563
    else {
564
      System.out.println("No action taken");
565
    }
566
  }
567
  
568

    
569
  /**
570
   * Save the current Harvest List as a potentially different file name.
571
   */
572
  void fileSaveAs() {
573
    int returnVal;
574
    
575
    returnVal = fileChooser.showOpenDialog(HarvestListEditor.this);
576
    
577
    if (returnVal == JFileChooser.APPROVE_OPTION) {
578
      harvestListFile = fileChooser.getSelectedFile();
579
      writeFile(harvestListFile);
580
      setTitle(title + ": " + harvestListFile.getName());
581
      saveMenuItem.setEnabled(true);
582
      harvestListHasChanged = false;
583
    }
584
  }
585
  
586
  
587
  /**
588
   * Validate the Harvest List that is currently stored in the table. This is
589
   * implemented by writing the Harvest List to a temporary file and then
590
   * running the SAX parser on the temporary file.
591
   */
592
  void fileValidate() {
593
    FileInputStream fis;
594
    HarvestListHandler harvestListHandler = new HarvestListHandler();
595
    InputStreamReader inputStreamReader;
596
    boolean loadHarvestList = false;
597
    boolean validateHarvestList = true;
598
    
599
    writeFile(tempFile);
600
    
601
    try {
602
      fis = new FileInputStream(tempFile);
603
      inputStreamReader = new InputStreamReader(fis);
604
      harvestListHandler.runParser(this, inputStreamReader, schemaLocation,
605
                                   loadHarvestList, validateHarvestList);
606
      fis.close();
607
      tempFile.delete();
608
      harvestListMessage("Harvest List is valid");
609
    }
610
    catch (SAXException e) {
611
      harvestListMessage("Validation error: " + e.getMessage());
612
    }
613
    catch (ClassNotFoundException e) {
614
      System.out.println("ClassNotFoundException: " + e.getMessage());
615
    }
616
    catch (IOException ioe) {
617
      System.out.println("Error opening file: " + ioe.getMessage());
618
    }
619
  }
620
  
621

    
622
  /**
623
   * Displays a short message in a dialog box.
624
   * 
625
   * @param message       the message text
626
   */
627
  void harvestListMessage(String message) {
628
    JOptionPane.showMessageDialog(this, message);
629
  }
630
  
631

    
632
  /**
633
   * Initializes the Harvest List table, filling column 0 with row numbers.
634
   * This is a non-editable column, so its values should never change after
635
   * this point.
636
   */
637
  void initHarvestList() {
638
    for (int rowIndex = 0; rowIndex < numRows; rowIndex++) {
639
      tableModel.setValueAt(new Integer(rowIndex + 1).toString(), rowIndex, 0);
640
    }
641
  }
642
  
643

    
644
  /**
645
   * Determines whether the clipboard is currently empty. The clipboard is
646
   * empty if all five of the fields are empty.
647
   * 
648
   * @return      true if the clipboard is empty, else false
649
   */
650
  boolean isEmptyClipboard() {
651
    boolean isEmpty = true;
652
    
653
    isEmpty = isEmpty && (clipboardScope.equals(""));
654
    isEmpty = isEmpty && (clipboardIdentifier.equals(""));
655
    isEmpty = isEmpty && (clipboardRevision.equals(""));
656
    isEmpty = isEmpty && (clipboardDocumentType.equals(""));
657
    isEmpty = isEmpty && (clipboardDocumentURL.equals(""));
658
    
659
    return isEmpty;
660
  }
661
    
662

    
663
  /**
664
   * Determines whether a given row in the table is empty. A row is empty if
665
   * all five of its fields contain either null or "".
666
   * 
667
   * @param rowIndex    the index to the row in the table that is being checked
668
   * @return            true if the row is empty, else false
669
   */
670
  boolean isEmptyRow(int rowIndex) {
671
    boolean isEmpty = true;
672
    String scope = (String) tableModel.getValueAt(rowIndex, 1);
673
    String identifier = (String) tableModel.getValueAt(rowIndex, 2);
674
    String revision = (String) tableModel.getValueAt(rowIndex, 3);
675
    String documentType = (String) tableModel.getValueAt(rowIndex, 4);    
676
    String documentURL = (String) tableModel.getValueAt(rowIndex, 5);
677
    
678
    isEmpty = isEmpty && ((scope == null) || (scope.equals("")));
679
    isEmpty = isEmpty && ((identifier == null) || (identifier.equals("")));
680
    isEmpty = isEmpty && ((revision == null) || (revision.equals("")));
681
    isEmpty = isEmpty && ((documentType == null) || (documentType.equals("")));
682
    isEmpty = isEmpty && ((documentURL == null) || (documentURL.equals("")));
683
    
684
    return isEmpty;
685
  }
686
  
687

    
688
  /**
689
   * Loads the Harvest List from a file. Parses the file using the inner class,
690
   * HarvestListHandler, a SAX parser.
691
   * 
692
   * @param harvestList  the File to be loaded
693
   * @throws ParserConfigurationException
694
   */
695
  void loadHarvestList(File harvestList) throws ParserConfigurationException {
696
    HarvestListHandler harvestListHandler = new HarvestListHandler();
697
    FileInputStream fis;
698
    InputStreamReader inputStreamReader;
699
    boolean loadHarvestList = true;
700
    boolean validateHarvestList = false;
701

    
702
    try {
703
      fis = new FileInputStream(harvestList);
704
      inputStreamReader = new InputStreamReader(fis);
705
      //System.out.println("Opened file successfully.");
706
      harvestListHandler.runParser(this, inputStreamReader, schemaLocation,
707
                                   loadHarvestList, validateHarvestList);
708
      fis.close();
709
      setTitle(title + ": " + harvestListFile.getName());
710
    }
711
    catch (SAXException e) {
712
      System.out.println("Error parsing Harvest List: " + e.getMessage());
713
    }
714
    catch (ClassNotFoundException e) {
715
      System.out.println("ClassNotFoundException: " + e.getMessage());
716
    }
717
    catch (IOException ioe) {
718
      System.out.println("Error opening file: " + ioe.getMessage());
719
    }
720
  }
721
  
722

    
723
  /**
724
   * Loads properties from the .harvestListEditor file in the user's home
725
   * directory.
726
   */
727
  void loadProperties () {
728
    String homedir = System.getProperty("user.home");
729
    File propertiesFile = new File(homedir, ".harvestListEditor");
730

    
731
    properties = new Properties();
732

    
733
    if (propertiesFile.exists()) {
734
      try {
735
        properties.load(new FileInputStream(propertiesFile));
736
        defaultHarvestList = properties.getProperty("defaultHarvestList");
737
        defaultDocumentType = properties.getProperty("defaultDocumentType");
738
        defaultDocumentURL = properties.getProperty("defaultDocumentURL");
739
        defaultIdentifier = properties.getProperty("defaultIdentifier");
740
        defaultRevision = properties.getProperty("defaultRevision");
741
        defaultScope = properties.getProperty("defaultScope");    
742
      }
743
      catch (IOException ioe) {
744
        System.out.println("Error loading properties file: " + ioe.getMessage());
745
      }  
746
    }
747
  }
748
  
749

    
750
  /**
751
   * Enables or disables buttons depending on the state of the currently
752
   * selected row and the state of the clipboard.
753
   * 
754
   * @param rowIndex       the index of the currently selected row
755
   */
756
  void manageButtons(int rowIndex) {
757
    
758
    if (isEmptyRow(rowIndex)) {
759
      // Selected row is empty, so disable cut and copy
760
      cutButton.setEnabled(false);
761
      copyButton.setEnabled(false);
762
    }
763
    else {
764
      // Selected row is not empty, so enable cut and copy
765
      cutButton.setEnabled(true);
766
      copyButton.setEnabled(true);
767
    }
768

    
769
    if (isEmptyClipboard()) {
770
      // Clipboard is empty, so disable paste
771
      pasteButton.setEnabled(false);
772
    }
773
    else {
774
      // Clipboard is not empty, so enable paste
775
      pasteButton.setEnabled(true);
776
    }
777

    
778
    // Paste Defaults button is enabled whenever a row is selected    
779
    pasteDefaultsButton.setEnabled(true);
780
  }
781
  
782

    
783
  /**
784
   * Pastes the clipboard values into the specified row.
785
   * 
786
   * @param rowIndex      the index of the row that is being pasted to
787
   */
788
  void pasteRow(int rowIndex) {
789
    tableModel.setValueAt(clipboardScope,        rowIndex, 1);
790
    tableModel.setValueAt(clipboardIdentifier,   rowIndex, 2);
791
    tableModel.setValueAt(clipboardRevision,     rowIndex, 3);
792
    tableModel.setValueAt(clipboardDocumentType, rowIndex, 4);
793
    tableModel.setValueAt(clipboardDocumentURL,  rowIndex, 5);
794
  }
795
  
796

    
797
  /**
798
   * Pastes the default values into the specified row.
799
   * 
800
   * @param rowIndex      the index of the row that is being pasted to
801
   */
802
  void pasteDefaultValues(int rowIndex) {
803
    tableModel.setValueAt(defaultScope,        rowIndex, 1);
804
    tableModel.setValueAt(defaultIdentifier,   rowIndex, 2);
805
    tableModel.setValueAt(defaultRevision,     rowIndex, 3);
806
    tableModel.setValueAt(defaultDocumentType, rowIndex, 4);
807
    tableModel.setValueAt(defaultDocumentURL,  rowIndex, 5);
808
  }
809
  
810

    
811
  /**
812
   * Dialog to determine whether user wants to save changes before proceeding.
813
   * 
814
   * @return   integer value that determines whether the user responded with
815
   *           "Yes", "No", or "Cancel"
816
   */
817
  int saveChangesDialog () {
818
    Object[] options = {"Yes", "No", "Cancel"};
819
    int value;
820
    
821
    value = JOptionPane.showOptionDialog(null,
822
                                         "Save Changes?",
823
                                         "Warning",
824
                                         JOptionPane.DEFAULT_OPTION,
825
                                         JOptionPane.WARNING_MESSAGE,
826
                                         null,
827
                                         options,
828
                                         options[0]); // default option
829
    return value;
830
  }
831
  
832

    
833
  /**
834
   * Writes the contents of the table to file as XML.
835
   * 
836
   * @param harvestList       the File object to write to
837
   */
838
  void writeFile(File harvestList) {
839
    try {
840
      PrintWriter out = new PrintWriter(new FileWriter(harvestList));
841
      writeHarvestList(out);
842
    }
843
    catch (IOException ioe) {
844
      System.out.println("IOException: " + ioe.getMessage());
845
    }
846
  }
847
  
848

    
849
  /**
850
   * Writes the contents of the table to a PrintWriter.
851
   * 
852
   * @param out       the PrintWriter to write the Harvest List to
853
   */
854
  void writeHarvestList(PrintWriter out) {
855
      out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
856
      out.println("");
857
      out.println(
858
       "<hrv:harvestList xmlns:hrv=\"eml://ecoinformatics.org/harvestList\" >");
859

    
860
      for (int i = 0; i < numRows; i++) {
861
        if (!isEmptyRow(i)) {
862
          writeRow(out, i);
863
        }
864
      }
865

    
866
      out.println("");
867
      out.println("</hrv:harvestList>");
868
      out.close();
869
  }
870
  
871

    
872
  /**
873
   * Writes a row of the table to file. A row corresponds to a single
874
   * <Document> element in the Harvest List.
875
   * 
876
   * @param out       the PrintWriter object for the file
877
   * @param rowIndex  the index of the table row that is being written to file
878
   */
879
  void writeRow(PrintWriter out, int rowIndex) {
880
    int indentLevel = 6;
881
    String scope = (String) tableModel.getValueAt(rowIndex, 1);
882
    String identifier = (String) tableModel.getValueAt(rowIndex, 2);
883
    String revision = (String) tableModel.getValueAt(rowIndex, 3);
884
    String documentType = (String) tableModel.getValueAt(rowIndex, 4);    
885
    String documentURL = (String) tableModel.getValueAt(rowIndex, 5);
886

    
887
    out.println("");
888
    out.println("  <document>");
889
    out.println("    <docid>");
890
    out.println(composeLine(indentLevel, "scope", scope));
891
    out.println(composeLine(indentLevel, "identifier", identifier));
892
    out.println(composeLine(indentLevel, "revision", revision));
893
    out.println("    </docid>");
894
    indentLevel = 4;
895
    out.println(composeLine(indentLevel, "documentType", documentType));
896
    out.println(composeLine(indentLevel, "documentURL", documentURL));
897
    out.println("  </document>");
898
  }
899
  
900

    
901
  /*
902
   * Inner class: HarvestListTableModel
903
   */
904
    class HarvestListTableModel extends AbstractTableModel {
905
      final boolean DEBUG = false;
906
      // Column names for the JTable  
907
      private String[] columnNames = {
908
                          "Row #",
909
                          "Scope",
910
                          "Identifier",
911
                          "Revision",
912
                          "Document Type",
913
                          "Document URL"
914
                         };
915

    
916
      private Object[][] data = new Object[numRows][numColumns];
917

    
918
        public int getColumnCount() {
919
            return columnNames.length;
920
        }
921

    
922
        public int getRowCount() {
923
            return data.length;
924
        }
925

    
926
        public String getColumnName(int col) {
927
            return columnNames[col];
928
        }
929

    
930
        public Object getValueAt(int row, int col) {
931
            return data[row][col];
932
        }
933

    
934
        /*
935
         * JTable uses this method to determine the default renderer/
936
         * editor for each cell.  If we didn't implement this method,
937
         * then the last column would contain text ("true"/"false"),
938
         * rather than a check box.
939
         */
940
        public Class getColumnClass(int c) {
941
            return getValueAt(0, c).getClass();
942
        }
943

    
944
        /*
945
         * Don't need to implement this method unless your table's
946
         * editable.
947
         */
948
        public boolean isCellEditable(int row, int col) {
949
            //Note that the data/cell address is constant,
950
            //no matter where the cell appears onscreen.
951
            if (col < 1) {
952
                return false;
953
            } else {
954
                return true;
955
            }
956
        }
957

    
958
        /*
959
         * Don't need to implement this method unless your table's
960
         * data can change.
961
         */
962
        public void setValueAt(Object value, int row, int col) {
963
            if (DEBUG) {
964
                System.out.println("Setting value at " + row + "," + col
965
                                   + " to " + value
966
                                   + " (an instance of "
967
                                   + value.getClass() + ")");
968
            }
969

    
970
            data[row][col] = value;
971
            fireTableCellUpdated(row, col);
972

    
973
            if (DEBUG) {
974
                System.out.println("New value of data:");
975
                printDebugData();
976
            }
977
        }
978

    
979
        private void printDebugData() {
980
            int numRows = getRowCount();
981
            int numCols = getColumnCount();
982

    
983
            for (int i=0; i < numRows; i++) {
984
                System.out.print("    row " + i + ":");
985
                for (int j=0; j < numCols; j++) {
986
                    System.out.print("  " + data[i][j]);
987
                }
988
                System.out.println();
989
            }
990
            System.out.println("--------------------------");
991
        }
992
    }
993

    
994
  /**
995
   * This inner class extends DefaultHandler. It parses the Harvest List file,
996
   * writing a new row to the table every time it encounters a </Document>
997
   * end tag.
998
   */
999
  class HarvestListHandler extends DefaultHandler implements ErrorHandler {
1000
  
1001
    public String scope;
1002
    public int identifier;
1003
    public String identifierString;
1004
    public String documentType;
1005
    private HarvestListEditor harvestListEditor;
1006
    boolean loadHarvestList;
1007
    public int revision;
1008
    public String revisionString;
1009
    private int rowIndex = 0;
1010
    public String documentURL;
1011
    private String currentQname;
1012
    public final static String DEFAULT_PARSER = 
1013
           "org.apache.xerces.parsers.SAXParser";
1014
    private boolean schemaValidate = true;
1015
    private boolean validateHarvestList;
1016
	
1017

    
1018
	  /**
1019
     * This method is called for any plain text within an element.
1020
     * It parses the value for any of the following elements:
1021
     * <scope>, <identifier>, <revision>, <documentType>, <documentURL>
1022
     * 
1023
     * @param ch          the character array holding the parsed text
1024
     * @param start       the start index
1025
     * @param length      the text length
1026
     * 
1027
     */
1028
    public void characters (char ch[], int start, int length) {
1029
      String s = new String(ch, start, length);
1030
 
1031
      if (length > 0) {           
1032
        if (currentQname.equals("scope")) {
1033
          scope += s;
1034
        }
1035
        else if (currentQname.equals("identifier")) {
1036
          identifierString += s;
1037
        }
1038
        else if (currentQname.equals("revision")) {
1039
          revisionString += s;
1040
        }
1041
        else if (currentQname.equals("documentType")) {
1042
          documentType += s;
1043
        }
1044
        else if (currentQname.equals("documentURL")) {
1045
          documentURL += s;
1046
        }
1047
      }
1048
    }
1049

    
1050

    
1051
    /** 
1052
     * Handles an end-of-document event.
1053
     */
1054
    public void endDocument () {
1055
    }
1056

    
1057

    
1058
    /** 
1059
     * Handles an end-of-element event. If the end tag is </Document>, then
1060
     * creates a new HarvestDocument object and pushes it to the document
1061
     * list.
1062
     * 
1063
     * @param uri
1064
     * @param localname
1065
     * @param qname
1066
     */
1067
    public void endElement(String uri, 
1068
                           String localname,
1069
                           String qname) {
1070
      
1071
      HarvestDocument harvestDocument;
1072
      
1073
      if (qname.equals("identifier")) {
1074
        identifier = Integer.parseInt(identifierString);
1075
      }
1076
      else if (qname.equals("revision")) {
1077
        revision = Integer.parseInt(revisionString);
1078
      }
1079
      else if (qname.equals("document")) {
1080
        if (loadHarvestList) {
1081
          harvestListEditor.addRow(rowIndex, scope, identifierString, 
1082
                                   revisionString, documentType, documentURL);
1083
        }
1084

    
1085
        rowIndex++;
1086
      }
1087

    
1088
      currentQname = "";
1089
    }
1090

    
1091

    
1092
    /**
1093
     * Method for handling errors during a parse
1094
     *
1095
     * @param exception         The parsing error
1096
     * @exception SAXException  Description of Exception
1097
     */
1098
     public void error(SAXParseException e) throws SAXParseException {
1099
        System.out.println("SAXParseException: " + e.getMessage());
1100
        throw e;
1101
    }
1102

    
1103

    
1104
    /**
1105
     * Run the validating parser
1106
     *
1107
     * @param xml             the xml stream to be validated
1108
     * @schemaLocation        relative path the to XML Schema file, e.g. "."
1109
     * @exception IOException thrown when test files can't be opened
1110
     * @exception ClassNotFoundException thrown when SAX Parser class not found
1111
     * @exception SAXException
1112
     * @exception SAXParserException
1113
     */
1114
    public void runParser(HarvestListEditor harvestListEditor,
1115
                          Reader xml, 
1116
                          String schemaLocation,
1117
                          boolean loadHarvestList,
1118
                          boolean validateHarvestList)
1119
           throws IOException, ClassNotFoundException,
1120
                  SAXException, SAXParseException {
1121

    
1122
      // Get an instance of the parser
1123
      XMLReader parser;
1124
      
1125
      this.harvestListEditor = harvestListEditor;
1126
      this.loadHarvestList = loadHarvestList;
1127
      this.validateHarvestList = validateHarvestList;
1128
      this.rowIndex = 0;
1129

    
1130
      parser = XMLReaderFactory.createXMLReader(DEFAULT_PARSER);
1131
      // Set Handlers in the parser
1132
      parser.setContentHandler((ContentHandler)this);
1133
      parser.setErrorHandler((ErrorHandler)this);
1134
      parser.setFeature("http://xml.org/sax/features/namespaces", true);
1135
      parser.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
1136
      parser.setFeature("http://xml.org/sax/features/validation", true);
1137
      parser.setProperty(
1138
              "http://apache.org/xml/properties/schema/external-schemaLocation", 
1139
              schemaLocation);
1140

    
1141
      if (schemaValidate) {
1142
        parser.setFeature("http://apache.org/xml/features/validation/schema", 
1143
                          true);
1144
      }
1145
    
1146
      // Parse the document
1147
      parser.parse(new InputSource(xml));
1148
    }
1149

    
1150

    
1151
    /**
1152
     * Handles a start-of-document event.
1153
     */
1154
    public void startDocument () {
1155
      //System.out.println("Started parsing Harvest List");
1156
    }
1157

    
1158

    
1159
    /** 
1160
     * Handles a start-of-element event.
1161
     * 
1162
     * @param uri
1163
     * @param localname
1164
     * @param qname
1165
     * @param attributes
1166
     */
1167
    public void startElement(String uri, 
1168
                             String localname,
1169
                             String qname,
1170
                             Attributes attributes) {
1171
      
1172
      currentQname = qname;
1173

    
1174
      if (qname.equals("scope")) {
1175
        scope = "";
1176
      }
1177
      else if (qname.equals("identifier")) {
1178
        identifierString = "";
1179
      }
1180
      else if (qname.equals("revision")) {
1181
        revisionString = "";
1182
      }
1183
      else if (qname.equals("documentType")) {
1184
        documentType = "";
1185
      }
1186
      else if (qname.equals("documentURL")) {
1187
        documentURL = "";
1188
      }
1189
    }
1190
  }
1191
}
(3-3/10)