Project

General

Profile

1 2177 costa
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2004 University of New Mexico and the
4
 *                  Regents of the University of California
5
 *
6
 *   '$Author$'
7
 *     '$Date$'
8
 * '$Revision$'
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 2179 costa
import java.util.Properties;
41 2177 costa
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 2179 costa
import javax.swing.table.AbstractTableModel;
56 2177 costa
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 2179 costa
  final int numColumns = 6;
98 2177 costa
  final int numRows = 300;
99
	JButton pasteButton;
100
	JButton pasteDefaultsButton;
101 2179 costa
  Properties properties;
102 2177 costa
  private String schemaLocation =
103
    "eml://ecoinformatics.org/harvestList ../../lib/harvester/harvestList.xsd";
104
  int selectedRow = -1;
105
  final JTable table;
106
  TableModel tableModel;
107 2179 costa
  File tempFile;
108
  String title = "Harvest List Editor";
109 2177 costa
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 2179 costa
  JMenuItem validateMenuItem = new JMenuItem("Validate");
117 2177 costa
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 2179 costa
142
    loadProperties();
143 2177 costa
		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 2179 costa
		validateMenuItem.setAccelerator(KeyStroke.getKeyStroke('V',
176
									                                     java.awt.Event.CTRL_MASK,
177
                                                       false));
178
		validateMenuItem.addActionListener(this);
179
		fileMenu.add(validateMenuItem);
180
181 2177 costa
		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 2179 costa
    table = new JTable(new HarvestListTableModel());
192 2177 costa
    table.setPreferredScrollableViewportSize(new Dimension(900, 300));
193
    tableColumn = table.getColumnModel().getColumn(0);
194 2179 costa
    tableColumn.setPreferredWidth(30);
195
    tableColumn = table.getColumnModel().getColumn(1);
196 2177 costa
    tableColumn.setPreferredWidth(200);
197
    tableColumn = table.getColumnModel().getColumn(2);
198
    tableColumn.setPreferredWidth(50);
199
    tableColumn = table.getColumnModel().getColumn(3);
200 2179 costa
    tableColumn.setPreferredWidth(50);
201 2177 costa
    tableColumn = table.getColumnModel().getColumn(4);
202 2179 costa
    tableColumn.setPreferredWidth(250);
203
    tableColumn = table.getColumnModel().getColumn(5);
204
    tableColumn.setPreferredWidth(320);
205 2177 costa
206
    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
207
    tableModel = table.getModel();
208 2179 costa
    initHarvestList();
209 2177 costa
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 2179 costa
    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 2177 costa
		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 2179 costa
		else if ((e.getActionCommand()).equals("Validate")) {
345
      fileValidate();
346
    }
347 2177 costa
    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 2179 costa
    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 2177 costa
  }
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 2179 costa
    final String nil = "";
414 2177 costa
415
    tableModel.setValueAt(nil, rowIndex, 1);
416
    tableModel.setValueAt(nil, rowIndex, 2);
417
    tableModel.setValueAt(nil, rowIndex, 3);
418 2179 costa
    tableModel.setValueAt(nil, rowIndex, 4);
419
    tableModel.setValueAt(nil, rowIndex, 5);
420 2177 costa
  }
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 2179 costa
    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 2177 costa
  }
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 2179 costa
    setTitle(title + ": (Untitled)");
506 2177 costa
    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 2179 costa
      setTitle(title + ": " + harvestListFile.getName());
577 2177 costa
      saveMenuItem.setEnabled(true);
578
      harvestListHasChanged = false;
579
    }
580
  }
581
582 2179 costa
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 2177 costa
618
  /**
619 2179 costa
   * 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 2177 costa
   * 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 2179 costa
    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 2177 costa
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 2179 costa
    boolean loadHarvestList = true;
696
    boolean validateHarvestList = false;
697 2177 costa
698
    try {
699
      fis = new FileInputStream(harvestList);
700
      inputStreamReader = new InputStreamReader(fis);
701 2179 costa
      //System.out.println("Opened file successfully.");
702
      harvestListHandler.runParser(this, inputStreamReader, schemaLocation,
703
                                   loadHarvestList, validateHarvestList);
704 2177 costa
      fis.close();
705 2179 costa
      setTitle(title + ": " + harvestListFile.getName());
706 2177 costa
    }
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 2179 costa
   * 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 2177 costa
   * 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 2179 costa
    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 2177 costa
  }
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 2179 costa
    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 2177 costa
  }
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 2179 costa
      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 2177 costa
      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 2179 costa
    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 2177 costa
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 2179 costa
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 2177 costa
  /**
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 2179 costa
    boolean loadHarvestList;
1001 2177 costa
    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 2179 costa
    private boolean validateHarvestList;
1010 2177 costa
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 2179 costa
        if (loadHarvestList) {
1075
          harvestListEditor.addRow(rowIndex, scope, identifierString,
1076
                                   revisionString, documentType, documentURL);
1077
        }
1078
1079 2177 costa
        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 2179 costa
                          String schemaLocation,
1111
                          boolean loadHarvestList,
1112
                          boolean validateHarvestList)
1113 2177 costa
           throws IOException, ClassNotFoundException,
1114
                  SAXException, SAXParseException {
1115
1116
      // Get an instance of the parser
1117
      XMLReader parser;
1118
1119
      this.harvestListEditor = harvestListEditor;
1120 2179 costa
      this.loadHarvestList = loadHarvestList;
1121
      this.validateHarvestList = validateHarvestList;
1122 2177 costa
      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 2179 costa
      //System.out.println("Started parsing Harvest List");
1150 2177 costa
    }
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
}