Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents an XML document
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Matt Jones
7
 *    Release: @release@
8
 *
9
 *   '$Author: bojilova $'
10
 *     '$Date: 2000-09-13 13:09:31 -0700 (Wed, 13 Sep 2000) $'
11
 * '$Revision: 447 $'
12
 */
13

    
14
package edu.ucsb.nceas.metacat;
15

    
16
import java.sql.*;
17
import java.io.File;
18
import java.io.FileReader;
19
import java.io.IOException;
20
import java.io.PrintWriter;
21
import java.io.Reader;
22
import java.io.StringWriter;
23
import java.io.Writer;
24

    
25
import java.util.Iterator;
26
import java.util.Stack;
27
import java.util.TreeSet;
28

    
29
import org.xml.sax.AttributeList;
30
import org.xml.sax.ContentHandler;
31
import org.xml.sax.DTDHandler;
32
import org.xml.sax.EntityResolver;
33
import org.xml.sax.ErrorHandler;
34
import org.xml.sax.InputSource;
35
import org.xml.sax.XMLReader;
36
import org.xml.sax.SAXException;
37
import org.xml.sax.SAXParseException;
38
import org.xml.sax.helpers.XMLReaderFactory;
39

    
40
/**
41
 * A class that represents an XML document. It can be created with a simple
42
 * document identifier from a database connection.  It also will write an
43
 * XML text document to a database connection using SAX.
44
 */
45
public class DocumentImpl {
46

    
47
  static final int ALL = 1;
48
  static final int WRITE = 2;
49
  static final int READ = 4;
50

    
51
  private Connection conn = null;
52
  private String docid = null;
53
  private String docname = null;
54
  private String doctype = null;
55
  private String system_id = null;
56
  private long rootnodeid;
57
  private ElementNode rootNode = null;
58
  private String doctitle = null;
59
  private TreeSet nodeRecordList = null;
60

    
61
  /**
62
   * Constructor, creates document from database connection, used 
63
   * for reading the document
64
   *
65
   * @param conn the database connection from which to read the document
66
   * @param docid the identifier of the document to be created
67
   */
68
  public DocumentImpl(Connection conn, String docid) throws McdbException 
69
  {
70
    try {
71
      this.conn = conn;
72
      this.docid = docid;
73
  
74
      // Look up the document information
75
      getDocumentInfo(docid);
76
      
77
      // Download all of the document nodes using a single SQL query
78
      // The sort order of the records is determined by the NodeComparator
79
      // class, and needs to represent a depth-first traversal for the
80
      // toXml() method to work properly
81
      nodeRecordList = getNodeRecordList(rootnodeid);
82
  
83
    } catch (McdbException ex) {
84
      throw ex;
85
    } catch (Throwable t) {
86
      throw new McdbException("Error reading document " + docid + ".");
87
    }
88
  }
89

    
90
  /** 
91
   * Construct a new document instance, writing the contents to the database.
92
   * This method is called from DBSAXHandler because we need to know the
93
   * root element name for documents without a DOCTYPE before creating it.
94
   *
95
   * @param conn the JDBC Connection to which all information is written
96
   * @param rootnodeid - sequence id of the root node in the document
97
   * @param docname - the name of DTD, i.e. the name immediately following 
98
   *        the DOCTYPE keyword ( should be the root element name ) or
99
   *        the root element name if no DOCTYPE declaration provided
100
   *        (Oracle's and IBM parsers are not aware if it is not the 
101
   *        root element name)
102
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
103
   *                  following the PUBLIC keyword in DOCTYPE declaration or
104
   *                  the docname if no Public ID provided or
105
   *                  null if no DOCTYPE declaration provided
106
   *
107
   */
108
  public DocumentImpl(Connection conn, long rootnodeid, String docname, 
109
                      String doctype, String docid, String action, String user)
110
                      throws AccessionNumberException 
111
  {
112
    this.conn = conn;
113
    this.rootnodeid = rootnodeid;
114
    this.docname = docname;
115
    this.doctype = doctype;
116
    this.docid = docid;
117
    writeDocumentToDB(action, user);
118
  }
119

    
120
  /**
121
   * get the document name
122
   */
123
  public String getDocname() {
124
    return docname;
125
  }
126

    
127
  /**
128
   * get the document type (which is the PublicID)
129
   */
130
  public String getDoctype() {
131
    return doctype;
132
  }
133

    
134
  /**
135
   * get the system identifier
136
   */
137
  public String getSystemID() {
138
    return system_id;
139
  }
140

    
141
  /**
142
   * get the root node identifier
143
   */
144
  public long getRootNodeID() {
145
    return rootnodeid;
146
  }
147

    
148
  /** 
149
   * Get the document identifier (docid)
150
   */
151
  public String getDocID() {
152
    return docid;
153
  }
154

    
155

    
156
  /**
157
   * Print a string representation of the XML document
158
   */
159
  public String toString()
160
  {
161
    StringWriter docwriter = new StringWriter();
162
    this.toXml(docwriter);
163
    String document = docwriter.toString();
164
    return document;
165
  }
166

    
167
  /**
168
   * Get a text representation of the XML document as a string
169
   * This older algorithm uses a recursive tree of Objects to represent the
170
   * nodes of the tree.  Each object is passed the data for the document 
171
   * and searches all of the document data to find its children nodes and
172
   * recursively build.  Thus, because each node reads the whole document,
173
   * this algorithm is extremely slow for larger documents, and the time
174
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
175
   * better algorithm.
176
   */
177
  public String readUsingSlowAlgorithm()
178
  {
179
    StringBuffer doc = new StringBuffer();
180

    
181
    // Create the elements from the downloaded data in the TreeSet
182
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
183

    
184
    // Append the resulting document to the StringBuffer and return it
185
    doc.append("<?xml version=\"1.0\"?>\n");
186
      
187
    if (docname != null) {
188
      if ((doctype != null) && (system_id != null)) {
189
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
190
                   "\" \"" + system_id + "\">\n");
191
      } else {
192
        doc.append("<!DOCTYPE " + docname + ">\n");
193
      }
194
    }
195
    doc.append(rootNode.toString());
196
  
197
    return (doc.toString());
198
  }
199

    
200
  /**
201
   * Print a text representation of the XML document to a Writer
202
   *
203
   * @param pw the Writer to which we print the document
204
   */
205
  public void toXml(Writer pw)
206
  {
207
    PrintWriter out = null;
208
    if (pw instanceof PrintWriter) {
209
      out = (PrintWriter)pw;
210
    } else {
211
      out = new PrintWriter(pw);
212
    }
213

    
214
    MetaCatUtil util = new MetaCatUtil();
215
    
216
    Stack openElements = new Stack();
217
    boolean atRootElement = true;
218
    boolean previousNodeWasElement = false;
219

    
220
    // Step through all of the node records we were given
221
    Iterator it = nodeRecordList.iterator();
222
    while (it.hasNext()) {
223
      NodeRecord currentNode = (NodeRecord)it.next();
224
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
225
                          //" (" + currentNode.parentnodeid +
226
                          //", " + currentNode.nodeindex + 
227
                          //", " + currentNode.nodetype + 
228
                          //", " + currentNode.nodename + 
229
                          //", " + currentNode.nodedata + ")]");
230

    
231
      // Print the end tag for the previous node if needed
232
      //
233
      // This is determined by inspecting the parent nodeid for the
234
      // currentNode.  If it is the same as the nodeid of the last element
235
      // that was pushed onto the stack, then we are still in that previous
236
      // parent element, and we do nothing.  However, if it differs, then we
237
      // have returned to a level above the previous parent, so we go into
238
      // a loop and pop off nodes and print out their end tags until we get
239
      // the node on the stack to match the currentNode parentnodeid
240
      //
241
      // So, this of course means that we rely on the list of elements
242
      // having been sorted in a depth first traversal of the nodes, which
243
      // is handled by the NodeComparator class used by the TreeSet
244
      if (!atRootElement) {
245
        NodeRecord currentElement = (NodeRecord)openElements.peek();
246
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
247
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
248
            currentElement = (NodeRecord)openElements.pop();
249
            util.debugMessage("\n POPPED: " + currentElement.nodename);
250
            out.print("</" + currentElement.nodename + ">" );
251
            currentElement = (NodeRecord)openElements.peek();
252
          }
253
        }
254
      }
255

    
256
      // Handle the DOCUMENT node
257
      if (currentNode.nodetype.equals("DOCUMENT")) {
258
        out.println("<?xml version=\"1.0\"?>");
259
      
260
        if (docname != null) {
261
          if ((doctype != null) && (system_id != null)) {
262
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
263
                       "\" \"" + system_id + "\">");
264
          } else {
265
            out.println("<!DOCTYPE " + docname + ">");
266
          }
267
        }
268

    
269
      // Handle the ELEMENT nodes
270
      } else if (currentNode.nodetype.equals("ELEMENT")) {
271
        if (atRootElement) {
272
          atRootElement = false;
273
        } else {
274
          if (previousNodeWasElement) {
275
            out.print(">");
276
          }
277
        }
278
        openElements.push(currentNode);
279
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
280
        previousNodeWasElement = true;
281
        out.print("<" + currentNode.nodename);
282

    
283
      // Handle the ATTRIBUTE nodes
284
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
285
        out.print(" " + currentNode.nodename + "=\""
286
                 + currentNode.nodedata + "\"");
287
      } else if (currentNode.nodetype.equals("TEXT")) {
288
        if (previousNodeWasElement) {
289
          out.print(">");
290
        }
291
        out.print(currentNode.nodedata);
292
        previousNodeWasElement = false;
293

    
294
      // Handle the COMMENT nodes
295
      } else if (currentNode.nodetype.equals("COMMENT")) {
296
        if (previousNodeWasElement) {
297
          out.print(">");
298
        }
299
        out.print("<!--" + currentNode.nodedata + "-->");
300
        previousNodeWasElement = false;
301

    
302
      // Handle the PI nodes
303
      } else if (currentNode.nodetype.equals("PI")) {
304
        if (previousNodeWasElement) {
305
          out.print(">");
306
        }
307
        out.print("<?" + currentNode.nodename + " " +
308
                        currentNode.nodedata + "?>");
309
        previousNodeWasElement = false;
310

    
311
      // Handle any other node type (do nothing)
312
      } else {
313
        // Any other types of nodes are not handled.
314
        // Probably should throw an exception here to indicate this
315
      }
316
      out.flush();
317
    }
318

    
319
    // Print the final end tag for the root element
320
    NodeRecord currentElement = (NodeRecord)openElements.pop();
321
    util.debugMessage("\n POPPED: " + currentElement.nodename);
322
    out.print("</" + currentElement.nodename + ">" );
323
    out.flush();
324
  }
325

    
326
  /**
327
   * Look up the document type information from the database
328
   *
329
   * @param docid the id of the document to look up
330
   */
331
  private void getDocumentInfo(String docid) throws McdbException 
332
  {
333
    PreparedStatement pstmt;
334

    
335
    try {
336
      pstmt =
337
        conn.prepareStatement("SELECT docname,doctype,rootnodeid " +
338
                                "FROM xml_documents " +
339
                               "WHERE docid LIKE ?");
340
      // Bind the values to the query
341
      pstmt.setString(1, docid);
342

    
343
      pstmt.execute();
344
      ResultSet rs = pstmt.getResultSet();
345
      boolean tableHasRows = rs.next();
346
      if (tableHasRows) {
347
        this.docname    = rs.getString(1);
348
        this.doctype    = rs.getString(2);
349
        this.rootnodeid = rs.getLong(3);
350
      } 
351
      pstmt.close();
352

    
353
      if (this.doctype != null) {
354
        pstmt =
355
          conn.prepareStatement("SELECT system_id " +
356
                                  "FROM xml_catalog " +
357
                                 "WHERE public_id LIKE ?");
358
        // Bind the values to the query
359
        pstmt.setString(1, doctype);
360
  
361
        pstmt.execute();
362
        rs = pstmt.getResultSet();
363
        tableHasRows = rs.next();
364
        if (tableHasRows) {
365
          this.system_id  = rs.getString(1);
366
        } 
367
        pstmt.close();
368
      }
369
    } catch (SQLException e) {
370
      throw new McdbException("Error accessing database connection.", e);
371
    }
372

    
373
    if (this.docname == null) {
374
      throw new McdbDocNotFoundException("Document not found: " + docid);
375
    }
376
  }
377

    
378
  /**
379
   * Look up the node data from the database
380
   *
381
   * @param rootnodeid the id of the root node of the node tree to look up
382
   */
383
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
384
  {
385
    PreparedStatement pstmt;
386
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
387
    long nodeid = 0;
388
    long parentnodeid = 0;
389
    long nodeindex = 0;
390
    String nodetype = null;
391
    String nodename = null;
392
    String nodedata = null;
393

    
394
    try {
395
      pstmt =
396
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
397
           "nodetype,nodename,"+               
398
           "replace(" +
399
           "replace(" +
400
           "replace(nodedata,'&','&amp;') " +
401
           ",'<','&lt;') " +
402
           ",'>','&gt;') " +
403
           "FROM xml_nodes WHERE rootnodeid = ?");
404

    
405
      // Bind the values to the query
406
      pstmt.setLong(1, rootnodeid);
407

    
408
      pstmt.execute();
409
      ResultSet rs = pstmt.getResultSet();
410
      boolean tableHasRows = rs.next();
411
      while (tableHasRows) {
412
        nodeid = rs.getLong(1);
413
        parentnodeid = rs.getLong(2);
414
        nodeindex = rs.getLong(3);
415
        nodetype = rs.getString(4);
416
        nodename = rs.getString(5);
417
        nodedata = rs.getString(6);
418

    
419
        // add the data to the node record list hashtable
420
        NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid, 
421
                                   nodeindex, nodetype, nodename, nodedata);
422
        nodeRecordList.add(currentRecord);
423

    
424
        // Advance to the next node
425
        tableHasRows = rs.next();
426
      } 
427
      pstmt.close();
428

    
429
    } catch (SQLException e) {
430
      throw new McdbException("Error accessing database connection.", e);
431
    }
432

    
433
    if (nodeRecordList != null) {
434
      return nodeRecordList;
435
    } else {
436
      throw new McdbException("Error getting node data: " + docid);
437
    }
438
  }
439

    
440
  /**
441
   * Write an XML file to the database, given a filename
442
   *
443
   * @param conn the JDBC connection to the database
444
   * @param filename the filename to be loaded into the database
445
   * @param action the action to be performed (INSERT OR UPDATE)
446
   * @param docid the docid to use for the INSERT OR UPDATE
447
   */
448
  public static String write( Connection conn, String filename, String action, 
449
                              String docid, String user, String group )
450
                throws IOException, SQLException, ClassNotFoundException,
451
                       SAXException, SAXParseException, Exception {
452

    
453
    return write(conn, new FileReader(new File(filename).toString()), 
454
                  action, docid, user, group);
455
  }
456
  
457
  /**
458
   * Write an XML file to the database, given a Reader
459
   *
460
   * @param conn the JDBC connection to the database
461
   * @param xml the xml stream to be loaded into the database
462
   * @param action the action to be performed (INSERT OR UPDATE)
463
   * @param docid the docid to use for the INSERT OR UPDATE
464
   */
465
  public static String write( Connection conn, Reader xml, String action, 
466
                              String docid, String user, String group )
467
                throws IOException, SQLException, ClassNotFoundException,
468
                       SAXException, SAXParseException, Exception {
469

    
470
    if ( action.equals("UPDATE") ) {
471
      // Determine if the docid is OK for UPDATE
472
      AccessionNumber ac = new AccessionNumber();
473
      String newdocid = ac.generate(docid, "UPDATE");
474

    
475
      // check for 'write' permission for 'user' to update this document
476
      if ( !hasWritePermission(conn, docid, user, group) ) {
477
        throw new Exception("User " + user + 
478
              " does not have permission to update XML Document #" + docid);
479
      }          
480
    }
481

    
482
    try {
483
        XMLReader parser = initializeParser(conn, action, docid, user);
484
        conn.setAutoCommit(false);
485
        parser.parse(new InputSource(xml));
486
        conn.commit();
487
        conn.setAutoCommit(true);
488
        return docid;
489
      } catch (SAXParseException e) {
490
        conn.rollback();
491
        throw e;
492
      } catch (SAXException e) {
493

    
494
        // If its a problem with the accession number its ok, just the 
495
        // accession number was regenerated
496
        AccessionNumberGeneratedException ang = null;
497
        try {
498
          Exception embedded = e.getException();
499
          if ((embedded != null) && 
500
              (embedded instanceof AccessionNumberGeneratedException)) {
501
            ang = (AccessionNumberGeneratedException)e.getException();
502
          }
503
        } catch (ClassCastException cce) {
504
          // Do nothing and just fall through to the ang != null test
505
        }
506
        if (ang != null) {
507
          conn.commit();
508
          conn.setAutoCommit(true);
509
          return (ang.getMessage());
510
        } else {
511
          conn.rollback();
512
          throw e;
513
        }
514
      } catch (Exception e) {
515
        conn.rollback();
516
        throw e;
517
      }
518
  }
519

    
520
  /**
521
   * Delete an XML file from the database (actually, just make it a revision
522
   * in the xml_revisions table)
523
   *
524
   * @param docid the ID of the document to be deleted from the database
525
   */
526
  public static void delete( Connection conn, String docid,
527
                                 String user, String group )
528
                throws IOException, SQLException, ClassNotFoundException, 
529
                       AccessionNumberException, Exception {
530

    
531
    // Determine if the docid is OK for DELETE
532
    AccessionNumber ac = new AccessionNumber();
533
    String newdocid = ac.generate(docid, "DELETE");
534

    
535
    // check for 'write' permission for 'user' to delete this document
536
    if ( !hasWritePermission(conn, docid, user, group) ) {
537
      throw new Exception("User " + user + 
538
              " does not have permission to delete XML Document #" + docid);
539
    }
540

    
541
    conn.setAutoCommit(false);
542
    // Copy the record to the xml_revisions table
543
    DocumentImpl.archiveDocRevision( conn, docid, user );
544

    
545
    // Now delete it from the xml_documents table
546
    Statement stmt = conn.createStatement();
547
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
548
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
549
    stmt.close();
550
    conn.commit();
551
  }
552
  
553
  /** Check for "write" permissions from DB connection */
554
  private static boolean hasWritePermission(Connection conn, String docid, 
555
                                     String user, String group) 
556
                                     throws SQLException {
557
    // b' of the command line invocation
558
    if ( (user == null) && (group == null) ) {
559
      return true;
560
    }
561

    
562
    PreparedStatement pstmt;
563
    // checking if user is owner of docid
564
    try {
565
      pstmt = conn.prepareStatement(
566
                   "SELECT 'x' FROM xml_documents " +
567
                   "WHERE docid LIKE ? AND user_owner LIKE ?");
568
      // Bind the values to the query
569
      pstmt.setString(1, docid);
570
      pstmt.setString(2, user);
571

    
572
      pstmt.execute();
573
      ResultSet rs = pstmt.getResultSet();
574
      boolean hasRow = rs.next();
575
      pstmt.close();
576
      if (hasRow) {
577
        return true;
578
      }
579
      
580
    } catch (SQLException e) {
581
      throw new 
582
        SQLException("Error checking document's owner: " + e.getMessage());
583
    }
584

    
585
    // checking access type from xml_access table
586
    int accesstype = 0;
587
    try {
588
      pstmt = conn.prepareStatement(
589
                   "SELECT access_type FROM xml_access " +
590
                   "WHERE docid LIKE ? " + 
591
                   "AND principal_name LIKE ? " +
592
                   "AND principal_type = 'user' " +
593
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
594
                                   "AND nvl(end_time,sysdate) " +
595
                   "UNION " +
596
                   "SELECT access_type FROM xml_access " +
597
                   "WHERE docid LIKE ? " + 
598
                   "AND principal_name LIKE ? " +
599
                   "AND principal_type = 'group' " +
600
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
601
                                   "AND nvl(end_time,sysdate)");
602
      // Bind the values to the query
603
      pstmt.setString(1, docid);
604
      pstmt.setString(2, user);
605
      pstmt.setString(3, docid);
606
      pstmt.setString(2, group);
607

    
608
      pstmt.execute();
609
      ResultSet rs = pstmt.getResultSet();
610
      boolean hasRows = rs.next();
611
      while ( hasRows ) {
612
        accesstype = rs.getInt(1);
613
        if ( (accesstype & WRITE) == WRITE ) {
614
          pstmt.close();
615
          return true;
616
        }
617
        hasRows = rs.next();
618
      }
619

    
620
      pstmt.close();
621
      return false;
622
      
623
    } catch (SQLException e) {
624
      throw new 
625
      SQLException("Error getting document's permissions: " + e.getMessage());
626
    }
627
  }
628

    
629
  /** creates SQL code and inserts new document into DB connection */
630
  private void writeDocumentToDB(String action, String user) 
631
               throws AccessionNumberException {
632
    try {
633
      PreparedStatement pstmt = null;
634

    
635
      if (action.equals("INSERT")) {
636
        AccessionNumber ac = new AccessionNumber();
637
        this.docid = ac.generate(docid, "INSERT");
638
        pstmt = conn.prepareStatement(
639
            "INSERT INTO xml_documents " +
640
            "(docid, rootnodeid, docname, doctype, " +
641
            "user_owner, user_updated, date_created, date_updated) " +
642
            "VALUES (?, ?, ?, ?, ?, ?, sysdate, sysdate)");
643
        // Bind the values to the query
644
        pstmt.setString(1, this.docid);
645
        pstmt.setLong(2, rootnodeid);
646
        pstmt.setString(3, docname);
647
        pstmt.setString(4, doctype);
648
        pstmt.setString(5, user);
649
        pstmt.setString(6, user);
650
      } else if (action.equals("UPDATE")) {
651

    
652
        // Save the old document entry in a backup table
653
        DocumentImpl.archiveDocRevision( conn, docid, user );
654

    
655
        // Delete index for the old version of docid
656
        // The new index is inserting on the next calls to DBSAXNode
657
        pstmt = conn.prepareStatement(
658
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
659
        pstmt.execute();
660
        pstmt.close();
661

    
662
        // Update the new document to reflect the new node tree
663
        pstmt = conn.prepareStatement(
664
            "UPDATE xml_documents " +
665
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
666
            "user_updated = ?, date_updated = sysdate WHERE docid LIKE ?");
667
        // Bind the values to the query
668
        pstmt.setLong(1, rootnodeid);
669
        pstmt.setString(2, docname);
670
        pstmt.setString(3, doctype);
671
        pstmt.setString(4, user);
672
        pstmt.setString(5, this.docid);
673
      } else {
674
        System.err.println("Action not supported: " + action);
675
      }
676

    
677
      // Do the insertion
678
      pstmt.execute();
679
      pstmt.close();
680

    
681
    } catch (SQLException e) {
682
      System.out.println(e.getMessage());
683
    } catch (AccessionNumberException ane) {
684
      MetaCatUtil.debugMessage("Invalid accession number.");
685
      MetaCatUtil.debugMessage(ane.getMessage());
686
      throw ane;
687
    } catch (Exception e) {
688
      System.out.println(e.getMessage());
689
    }
690
  }
691

    
692
  /**
693
   * Get the document title
694
   */
695
  public String getTitle() {
696
    return doctitle;
697
  }
698

    
699
  /**
700
   * Set the document title
701
   *
702
   * @param title the new title for the document
703
   */
704
  public void setTitle( String title ) {
705
    this.doctitle = title;
706
    try {
707
      PreparedStatement pstmt;
708
      pstmt = conn.prepareStatement(
709
            "UPDATE xml_documents " +
710
            " SET doctitle = ? " +
711
            "WHERE docid = ?");
712

    
713
      // Bind the values to the query
714
      pstmt.setString(1, doctitle);
715
      pstmt.setString(2, docid);
716

    
717
      // Do the insertion
718
      pstmt.execute();
719
      pstmt.close();
720
    } catch (SQLException e) {
721
      System.out.println(e.getMessage());
722
    }
723
  }
724

    
725
  /**
726
   * Look up the title of the first child element named "title"
727
   * and record it as the document title
728
   */
729
  public void setTitleFromChildElement() {
730
    String title = null;
731
    long assigned_id=0;
732
    PreparedStatement pstmt;
733
    try {
734
      pstmt = conn.prepareStatement(
735
              "SELECT nodedata FROM xml_nodes " +
736
              "WHERE nodetype = 'TEXT' " +
737
              "AND rootnodeid = ? " +
738
              "AND parentnodeid IN " +
739
              "  (SELECT nodeid FROM xml_nodes " +
740
              "  WHERE nodename = 'title' " +
741
              "  AND nodetype =  'ELEMENT' " +
742
              "  AND rootnodeid = ? ) " +
743
              "ORDER BY nodeid");
744

    
745
      // The above query might be slow, and probably will be because
746
      // it gets ALL of the title elements while searching for one
747
      // title in a small subtree but it avoids the problem of using
748
      // Oracle's Hierarchical Query syntax which is not portable --
749
      // the commented out SQL that follows shows an equivalent query
750
      // using Oracle-specific hierarchical query
751
      /*
752
      pstmt = conn.prepareStatement(
753
              "SELECT nodedata FROM xml_nodes " +
754
              "WHERE nodetype = 'TEXT' " +
755
              "AND parentnodeid IN " +
756
              "(SELECT nodeid FROM xml_nodes " +
757
              "WHERE nodename = 'title' " +
758
              "START WITH nodeid = ? " +
759
              "CONNECT BY PRIOR nodeid = parentnodeid)");
760
      */
761

    
762
      // Bind the values to the query
763
      pstmt.setLong(1, rootnodeid);
764
      pstmt.setLong(2, rootnodeid);
765

    
766
      pstmt.execute();
767
      ResultSet rs = pstmt.getResultSet();
768
      boolean tableHasRows = rs.next();
769
      if (tableHasRows) {
770
        title = rs.getString(1);
771
      }
772
      pstmt.close();
773
    } catch (SQLException e) {
774
      System.out.println("Error getting title: " + e.getMessage());
775
    }
776

    
777
    // assign the new title
778
    this.setTitle(title);
779
  }
780

    
781
  /** Save a document entry in the xml_revisions table */
782
  private static void archiveDocRevision(Connection conn, String docid,
783
                                         String user) throws SQLException {
784
    // create a record in xml_revisions table 
785
    // for that document as selected from xml_documents
786
    PreparedStatement pstmt = conn.prepareStatement(
787
      "INSERT INTO xml_revisions " +
788
        "(revisionid, docid, rootnodeid, docname, doctype, doctitle, " +
789
        "user_owner, user_updated, date_created, date_updated) " +
790
      "SELECT null, ?, rootnodeid, docname, doctype, doctitle," + 
791
        "user_owner, ?, sysdate, sysdate "+
792
      "FROM xml_documents " +
793
      "WHERE docid = ?");
794
    // Bind the values to the query and execute it
795
    pstmt.setString(1, docid);
796
    pstmt.setString(2, user);
797
    pstmt.setString(3, docid);
798
    pstmt.execute();
799
    pstmt.close();
800

    
801
  }
802

    
803
  /** Save a document entry in the xml_revisions table */
804
/*
805
  private static void archiveDocRevision(Connection conn, String docid,
806
                                         String user) throws SQLException {
807
    // First get all of the values we need
808
    long rnodeid = -1;
809
    String docname = null;
810
    String doctype = null;
811
    String doctitle = null;
812
    String user_owner = null;
813
    Date date_created = null;
814
    PreparedStatement pstmt = conn.prepareStatement(
815
      "SELECT rootnodeid,docname,doctype,doctitle,user_owner,date_created " +
816
      "FROM xml_documents " +
817
      "WHERE docid = ?");
818
    // Bind the values to the query and execute it
819
    pstmt.setString(1, docid);
820
    pstmt.execute();
821

    
822
    ResultSet rs = pstmt.getResultSet();
823
    boolean tableHasRows = rs.next();
824
    if (tableHasRows) {
825
      rnodeid      = rs.getLong(1);
826
      docname      = rs.getString(2);
827
      doctype      = rs.getString(3);
828
      doctitle     = rs.getString(4);
829
      user_owner   = rs.getString(5);
830
      date_created = rs.getDate(6);
831
    }
832
    pstmt.close();
833

    
834
    MetaCatUtil.debugMessage(new Long(rnodeid).toString());
835
    MetaCatUtil.debugMessage(docname);
836
    MetaCatUtil.debugMessage(doctitle);
837
    //MetaCatUtil.debugMessage(date_created.toString());
838

    
839
    // Next create the new record in the other table using the values selected
840
    pstmt = conn.prepareStatement(
841
       "INSERT INTO xml_revisions " +
842
       "(revisionid, docid, rootnodeid, docname, doctype, doctitle, " +
843
       "user_owner, user_updated, date_created, date_updated) " +
844
       "VALUES (null, ?, ?, ?, ?, ?, ?, ?, sysdate, sysdate)");
845
    // Bind the values to the query and execute it
846
    pstmt.setString(1, docid);
847
    pstmt.setLong(2, rnodeid);
848
    pstmt.setString(3, docname);
849
    pstmt.setString(4, doctype);
850
    pstmt.setString(5, doctitle);
851
    pstmt.setString(6, user_owner);
852
    pstmt.setString(7, user);
853
    //pstmt.setDate(6, date_created);
854
    pstmt.execute();
855
    pstmt.close();
856
  }
857
*/
858
  /**
859
   * Set up the parser handlers for writing the document to the database
860
   */
861
  private static XMLReader initializeParser(Connection conn,
862
                           String action, String docid, String user) {
863
    XMLReader parser = null;
864
    //
865
    // Set up the SAX document handlers for parsing
866
    //
867
    try {
868
      ContentHandler chandler   = new DBSAXHandler(conn, action, docid, user);
869
      EntityResolver dbresolver = new DBEntityResolver(conn, 
870
                                      (DBSAXHandler)chandler);
871
      DTDHandler dtdhandler     = new DBDTDHandler(conn);
872

    
873
      // Get an instance of the parser
874
      MetaCatUtil util = new MetaCatUtil();
875
      String parserName = util.getOption("saxparser");
876
      parser = XMLReaderFactory.createXMLReader(parserName);
877

    
878
      // Turn off validation
879
      parser.setFeature("http://xml.org/sax/features/validation", false);
880
      
881
      // Set Handlers in the parser
882
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
883
                         chandler);
884
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
885
                         chandler);
886
      parser.setContentHandler((ContentHandler)chandler);
887
      parser.setEntityResolver((EntityResolver)dbresolver);
888
      parser.setDTDHandler((DTDHandler)dtdhandler);
889
      parser.setErrorHandler((ErrorHandler)chandler);
890

    
891
    } catch (Exception e) {
892
       System.err.println(e.toString());
893
    }
894

    
895
    return parser;
896
  }
897

    
898
  /**
899
   * the main routine used to test the DBWriter utility.
900
   * <p>
901
   * Usage: java DocumentImpl <-f filename -a action -d docid>
902
   *
903
   * @param filename the filename to be loaded into the database
904
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
905
   * @param docid the id of the document to process
906
   */
907
  static public void main(String[] args) {
908
     
909
    try {
910
      String filename = null;
911
      String action   = null;
912
      String docid    = null;
913
      boolean showRuntime = false;
914
      boolean useOldReadAlgorithm = false;
915

    
916
      // Parse the command line arguments
917
      for ( int i=0 ; i < args.length; ++i ) {
918
        if ( args[i].equals( "-f" ) ) {
919
          filename =  args[++i];
920
        } else if ( args[i].equals( "-a" ) ) {
921
          action =  args[++i];
922
        } else if ( args[i].equals( "-d" ) ) {
923
          docid =  args[++i];
924
        } else if ( args[i].equals( "-t" ) ) {
925
          showRuntime = true;
926
        } else if ( args[i].equals( "-old" ) ) {
927
          useOldReadAlgorithm = true;
928
        } else {
929
          System.err.println
930
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
931
        }
932
      }
933
      
934
      // Check if the required arguments are provided
935
      boolean argsAreValid = false;
936
      if (action != null) {
937
        if (action.equals("INSERT")) {
938
          if (filename != null) {
939
            argsAreValid = true;
940
          } 
941
        } else if (action.equals("UPDATE")) {
942
          if ((filename != null) && (docid != null)) {
943
            argsAreValid = true;
944
          } 
945
        } else if (action.equals("DELETE")) {
946
          if (docid != null) {
947
            argsAreValid = true;
948
          } 
949
        } else if (action.equals("READ")) {
950
          if (docid != null) {
951
            argsAreValid = true;
952
          } 
953
        } 
954
      } 
955

    
956
      // Print usage message if the arguments are not valid
957
      if (!argsAreValid) {
958
        System.err.println("Wrong number of arguments!!!");
959
        System.err.println(
960
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename>");
961
        System.err.println(
962
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename>");
963
        System.err.println(
964
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
965
        System.err.println(
966
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
967
        return;
968
      }
969
      
970
      // Time the request if asked for
971
      double startTime = System.currentTimeMillis();
972
      
973
      // Open a connection to the database
974
      MetaCatUtil util = new MetaCatUtil();
975
      Connection dbconn = util.openDBConnection();
976

    
977
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
978
      if (action.equals("READ")) {
979
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
980
          if (useOldReadAlgorithm) {
981
            System.out.println(xmldoc.readUsingSlowAlgorithm());
982
          } else {
983
            xmldoc.toXml(new PrintWriter(System.out));
984
          }
985
      } else if (action.equals("DELETE")) {
986
        DocumentImpl.delete(dbconn, docid, null, null);
987
        System.out.println("Document deleted: " + docid);
988
      } else {
989
        String newdocid = DocumentImpl.write(dbconn, filename, action, docid,
990
                                                                  null, null);
991
        if ((docid != null) && (!docid.equals(newdocid))) {
992
          if (action.equals("INSERT")) {
993
            System.out.println("New document ID generated!!! ");
994
          } else if (action.equals("UPDATE")) {
995
            System.out.println("ERROR: Couldn't update document!!! ");
996
          }
997
        } else if ((docid == null) && (action.equals("UPDATE"))) {
998
          System.out.println("ERROR: Couldn't update document!!! ");
999
        }
1000
        System.out.println("Document processing finished for: " + filename
1001
              + " (" + newdocid + ")");
1002
      }
1003

    
1004
      double stopTime = System.currentTimeMillis();
1005
      double executionTime = (stopTime - startTime)/1000;
1006
      if (showRuntime) {
1007
        System.out.println("\n\nExecution time was: " + 
1008
                           executionTime + " seconds");
1009
      }
1010
    } catch (McdbException me) {
1011
      me.toXml(new PrintWriter(System.err));
1012
    } catch (AccessionNumberException ane) {
1013
      System.out.println("ERROR: Couldn't delete document!!! ");
1014
      System.out.println(ane.getMessage());
1015
    } catch (Exception e) {
1016
      System.err.println("EXCEPTION HANDLING REQUIRED");
1017
      System.err.println(e.getMessage());
1018
      e.printStackTrace(System.err);
1019
    }
1020
  }
1021
}
(15-15/27)