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-06-04 14:25:12 -0700 (Fri, 04 Jun 2004) $'
8
 * '$Revision: 2179 $'
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;
86
  String defaultDocumentURL;
87
  String defaultHarvestList;
88
  String defaultIdentifier;
89
  String defaultRevision;
90
  String defaultScope;
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
  int selectedRow = -1;
105
  final JTable table;
106
  TableModel tableModel;
107
  File tempFile;
108
  String title = "Harvest List Editor";
109
  
110
  // Menu items
111
  JMenuItem exitMenuItem = new JMenuItem("Exit");
112
  JMenuItem newMenuItem = new JMenuItem("New");
113
  JMenuItem openMenuItem = new JMenuItem("Open...");
114
  JMenuItem saveMenuItem = new JMenuItem("Save");
115
  JMenuItem saveAsMenuItem = new JMenuItem("Save As...");
116
  JMenuItem validateMenuItem = new JMenuItem("Validate");
117
  
118

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

    
128

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
314
    try {    
315
      tempFile = File.createTempFile("harvestListTemp", ".xml");
316
    }
317
    catch (IOException ioe) {
318
      System.out.println("Error creating temporary file: " + ioe.getMessage());
319
    }
320

    
321
		setVisible(true);
322
  }
323

    
324

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

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

    
373

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

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

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

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

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

    
449
  /**
450
   * Exit from the Harvest List Editor. Prompt to save changes if appropriate.
451
   */
452
  void fileExit() {
453
    int value;
454

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

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

    
503
    clearHarvestList();
504
    harvestListFile = null;
505
    setTitle(title + ": (Untitled)");
506
    saveMenuItem.setEnabled(false);
507
    harvestListHasChanged = false;    
508
  }
509
  
510

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

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

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

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

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

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

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

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

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

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

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

    
727
    properties = new Properties();
728

    
729
    try {
730
      properties.load(new FileInputStream(propertiesFile));
731
      defaultHarvestList = properties.getProperty("defaultHarvestList");
732
      defaultDocumentType = properties.getProperty("defaultDocumentType");
733
      defaultDocumentURL = properties.getProperty("defaultDocumentURL");
734
      defaultIdentifier = properties.getProperty("defaultIdentifier");
735
      defaultRevision = properties.getProperty("defaultRevision");
736
      defaultScope = properties.getProperty("defaultScope");    
737
    }
738
    catch (IOException ioe) {
739
      System.out.println("Error loading properties file: " + ioe.getMessage());
740
    }    
741
  }
742
  
743

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

    
763
    if (isEmptyClipboard()) {
764
      // Clipboard is empty, so disable paste
765
      pasteButton.setEnabled(false);
766
    }
767
    else {
768
      // Clipboard is not empty, so enable paste
769
      pasteButton.setEnabled(true);
770
    }
771

    
772
    // Paste Defaults button is enabled whenever a row is selected    
773
    pasteDefaultsButton.setEnabled(true);
774
  }
775
  
776

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

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

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

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

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

    
854
      for (int i = 0; i < numRows; i++) {
855
        if (!isEmptyRow(i)) {
856
          writeRow(out, i);
857
        }
858
      }
859

    
860
      out.println("");
861
      out.println("</hrv:harvestList>");
862
      out.close();
863
  }
864
  
865

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

    
881
    out.println("");
882
    out.println("  <document>");
883
    out.println("    <docid>");
884
    out.println(composeLine(indentLevel, "scope", scope));
885
    out.println(composeLine(indentLevel, "identifier", identifier));
886
    out.println(composeLine(indentLevel, "revision", revision));
887
    out.println("    </docid>");
888
    indentLevel = 4;
889
    out.println(composeLine(indentLevel, "documentType", documentType));
890
    out.println(composeLine(indentLevel, "documentURL", documentURL));
891
    out.println("  </document>");
892
  }
893
  
894

    
895
  /*
896
   * Inner class: HarvestListTableModel
897
   */
898
    class HarvestListTableModel extends AbstractTableModel {
899
      final boolean DEBUG = false;
900
      // Column names for the JTable  
901
      private String[] columnNames = {
902
                          "Row #",
903
                          "Scope",
904
                          "Identifier",
905
                          "Revision",
906
                          "Document Type",
907
                          "Document URL"
908
                         };
909

    
910
      private Object[][] data = new Object[numRows][numColumns];
911

    
912
        public int getColumnCount() {
913
            return columnNames.length;
914
        }
915

    
916
        public int getRowCount() {
917
            return data.length;
918
        }
919

    
920
        public String getColumnName(int col) {
921
            return columnNames[col];
922
        }
923

    
924
        public Object getValueAt(int row, int col) {
925
            return data[row][col];
926
        }
927

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

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

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

    
964
            data[row][col] = value;
965
            fireTableCellUpdated(row, col);
966

    
967
            if (DEBUG) {
968
                System.out.println("New value of data:");
969
                printDebugData();
970
            }
971
        }
972

    
973
        private void printDebugData() {
974
            int numRows = getRowCount();
975
            int numCols = getColumnCount();
976

    
977
            for (int i=0; i < numRows; i++) {
978
                System.out.print("    row " + i + ":");
979
                for (int j=0; j < numCols; j++) {
980
                    System.out.print("  " + data[i][j]);
981
                }
982
                System.out.println();
983
            }
984
            System.out.println("--------------------------");
985
        }
986
    }
987

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

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

    
1044

    
1045
    /** 
1046
     * Handles an end-of-document event.
1047
     */
1048
    public void endDocument () {
1049
    }
1050

    
1051

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

    
1079
        rowIndex++;
1080
      }
1081

    
1082
      currentQname = "";
1083
    }
1084

    
1085

    
1086
    /**
1087
     * Method for handling errors during a parse
1088
     *
1089
     * @param exception         The parsing error
1090
     * @exception SAXException  Description of Exception
1091
     */
1092
     public void error(SAXParseException e) throws SAXParseException {
1093
        System.out.println("SAXParseException: " + e.getMessage());
1094
        throw e;
1095
    }
1096

    
1097

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

    
1116
      // Get an instance of the parser
1117
      XMLReader parser;
1118
      
1119
      this.harvestListEditor = harvestListEditor;
1120
      this.loadHarvestList = loadHarvestList;
1121
      this.validateHarvestList = validateHarvestList;
1122
      this.rowIndex = 0;
1123

    
1124
      parser = XMLReaderFactory.createXMLReader(DEFAULT_PARSER);
1125
      // Set Handlers in the parser
1126
      parser.setContentHandler((ContentHandler)this);
1127
      parser.setErrorHandler((ErrorHandler)this);
1128
      parser.setFeature("http://xml.org/sax/features/namespaces", true);
1129
      parser.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
1130
      parser.setFeature("http://xml.org/sax/features/validation", true);
1131
      parser.setProperty(
1132
              "http://apache.org/xml/properties/schema/external-schemaLocation", 
1133
              schemaLocation);
1134

    
1135
      if (schemaValidate) {
1136
        parser.setFeature("http://apache.org/xml/features/validation/schema", 
1137
                          true);
1138
      }
1139
    
1140
      // Parse the document
1141
      parser.parse(new InputSource(xml));
1142
    }
1143

    
1144

    
1145
    /**
1146
     * Handles a start-of-document event.
1147
     */
1148
    public void startDocument () {
1149
      //System.out.println("Started parsing Harvest List");
1150
    }
1151

    
1152

    
1153
    /** 
1154
     * Handles a start-of-element event.
1155
     * 
1156
     * @param uri
1157
     * @param localname
1158
     * @param qname
1159
     * @param attributes
1160
     */
1161
    public void startElement(String uri, 
1162
                             String localname,
1163
                             String qname,
1164
                             Attributes attributes) {
1165
      
1166
      currentQname = qname;
1167

    
1168
      if (qname.equals("scope")) {
1169
        scope = "";
1170
      }
1171
      else if (qname.equals("identifier")) {
1172
        identifierString = "";
1173
      }
1174
      else if (qname.equals("revision")) {
1175
        revisionString = "";
1176
      }
1177
      else if (qname.equals("documentType")) {
1178
        documentType = "";
1179
      }
1180
      else if (qname.equals("documentURL")) {
1181
        documentURL = "";
1182
      }
1183
    }
1184
  }
1185
}
(3-3/10)