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-14 15:26:14 -0700 (Mon, 14 Jun 2004) $'
8
 * '$Revision: 2187 $'
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
  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
    else {
314
      fileNew();
315
    }
316

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

    
324
		setVisible(true);
325
  }
326

    
327

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

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

    
376

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
730
    properties = new Properties();
731

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1049

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

    
1056

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

    
1084
        rowIndex++;
1085
      }
1086

    
1087
      currentQname = "";
1088
    }
1089

    
1090

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

    
1102

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

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

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

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

    
1149

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

    
1157

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

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