Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents an XML node and its contents
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Matt Jones
7
 *
8
 *   '$Author: daigle $'
9
 *     '$Date: 2010-02-03 13:29:30 -0800 (Wed, 03 Feb 2010) $'
10
 * '$Revision: 5208 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat;
28

    
29
import java.sql.*;
30
import java.util.Hashtable;
31
import java.util.Enumeration;
32
import org.apache.log4j.Logger;
33
import org.xml.sax.SAXException;
34

    
35
import edu.ucsb.nceas.metacat.database.DBConnection;
36
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
37
import edu.ucsb.nceas.metacat.database.DatabaseService;
38

    
39
/**
40
 * A Class that represents an XML node and its contents and
41
 * can write its own representation to a database connection
42
 */
43
public class DBSAXNode extends BasicNode {
44

    
45
  private DBConnection	connection;
46
  private DBSAXNode	parentNode;
47
  private Logger logMetacat = Logger.getLogger(DBSAXNode.class);
48

    
49
  /**
50
   * Construct a new node instance for DOCUMENT nodes
51
   *
52
   * @param conn the JDBC Connection to which all information is written
53
   */
54
  public DBSAXNode (DBConnection conn, String docid) throws SAXException {
55

    
56
    super();
57
    this.connection = conn;
58
    this.parentNode = null;
59
    writeChildNodeToDB("DOCUMENT", null, null, docid);
60
    updateRootNodeID(getNodeID());
61
  }
62

    
63
  /**
64
   * Construct a new node instance for ELEMENT nodes
65
   *
66
   * @param conn the JDBC Connection to which all information is written
67
   * @param tagname the name of the node
68
   * @param parentNode the parent node for this node being created
69
   */
70
  public DBSAXNode (DBConnection conn, String qName, String lName,
71
                    DBSAXNode parentNode, long rootnodeid,
72
                    String docid, String doctype)
73
                                               throws SAXException {
74

    
75
    super(lName);
76
    this.connection = conn;
77
    this.parentNode = parentNode;
78
    setParentID(parentNode.getNodeID());
79
    setRootNodeID(rootnodeid);
80
    setDocID(docid);
81
    setNodeIndex(parentNode.incChildNum());
82
    writeChildNodeToDB("ELEMENT", qName, null, docid);
83
    //No writing XML Index from here. New Thread used instead.
84
    //updateNodeIndex(docid, doctype);
85
  }
86

    
87
  /**
88
   * Construct a new node instance for DTD nodes
89
   * This Node will write docname, publicId and systemId into db. Only
90
   * handle systemid  existed.(external dtd)
91
   *
92
   * @param conn the JDBC Connection to which all information is written
93
   * @param tagname the name of the node
94
   * @param parentNode the parent node for this node being created
95
   */
96
  public DBSAXNode (DBConnection conn, String docName, String publicId,
97
                    String systemId, DBSAXNode parentNode, long rootnodeid,
98
                    String docid) throws SAXException
99
  {
100

    
101
    super();
102
    this.connection = conn;
103
    this.parentNode = parentNode;
104
    setParentID(parentNode.getNodeID());
105
    setRootNodeID(rootnodeid);
106
    setDocID(docid);
107
    // insert dtd node only for external dtd definiation
108
    if (systemId != null)
109
    {
110
      //write docname to DB
111
      if (docName != null)
112
      {
113
        setNodeIndex(parentNode.incChildNum());
114
        writeDTDNodeToDB(DocumentImpl.DOCNAME, docName, docid);
115
      }
116
      //write publicId to DB
117
      if (publicId != null)
118
      {
119
        setNodeIndex(parentNode.incChildNum());
120
        writeDTDNodeToDB(DocumentImpl.PUBLICID, publicId, docid);
121
      }
122
      //write systemId to DB
123
      setNodeIndex(parentNode.incChildNum());
124
      writeDTDNodeToDB(DocumentImpl.SYSTEMID, systemId, docid);
125
    }
126
  }
127
  
128
  /** creates SQL code and inserts new node into DB connection */
129
  public long writeChildNodeToDB(String nodetype, String nodename,
130
                                 String data, String docid)
131
                                 throws SAXException {
132
    String limitedData = null;
133
    int leftover = 0;
134
    if (data != null) {
135
    	leftover = data.length();
136
    }
137
    int offset = 0;
138
    boolean moredata = true;
139
    long endNodeId = -1;
140

    
141
    // This loop deals with the case where there are more characters
142
    // than can fit in a single database text field (limit is
143
    // MAXDATACHARS). If the text to be inserted exceeds MAXDATACHARS,
144
    // write a series of nodes that are MAXDATACHARS long, and then the
145
    // final node contains the remainder
146
    while (moredata) {
147
        if (leftover > DBSAXHandler.MAXDATACHARS) {
148
        	limitedData = data.substring(offset, DBSAXHandler.MAXDATACHARS);
149
            leftover -= DBSAXHandler.MAXDATACHARS;
150
            offset += DBSAXHandler.MAXDATACHARS;
151
        } else {
152
        	limitedData = data;
153
            moredata = false;
154
        }
155
        endNodeId =  writeChildNodeToDBDataLimited(nodetype, nodename,
156
              limitedData, docid);
157
    }
158
    
159
    return endNodeId;
160
  }
161

    
162
  /** creates SQL code and inserts new node into DB connection */
163
  public long writeChildNodeToDBDataLimited(String nodetype, String nodename,
164
                                 String data, String docid)
165
                                 throws SAXException
166
  {
167
    long nid = -1;
168
    try
169
    {
170

    
171
      PreparedStatement pstmt;
172

    
173
      if (nodetype == "DOCUMENT") {
174
        pstmt = connection.prepareStatement(
175
            "INSERT INTO xml_nodes " +
176
            "(nodetype, nodename, nodeprefix, docid) " +
177
            "VALUES (?, ?, ?, ?)");
178

    
179
        // Increase DBConnection usage count
180
        connection.increaseUsageCount(1);
181
        logMetacat.debug("DBSAXNode.writeChildNodeToDBDataLimited - inserting doc name: " + nodename);
182
      } else {
183
          if(data != null && !data.trim().equals("")
184
             && !data.trim().equals("NaN") && !data.trim().equalsIgnoreCase("Infinity")){
185
              try{
186
                  double numberData = Double.parseDouble(data);
187
                  pstmt = connection.prepareStatement(
188
                      "INSERT INTO xml_nodes " +
189
                      "(nodetype, nodename, nodeprefix, docid, " +
190
                      "rootnodeid, parentnodeid, nodedata, nodeindex, nodedatanumerical) " +
191
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, "+ numberData +")");
192
              } catch (NumberFormatException nfe) {
193
                  pstmt = connection.prepareStatement(
194
                      "INSERT INTO xml_nodes " +
195
                      "(nodetype, nodename, nodeprefix, docid, " +
196
                      "rootnodeid, parentnodeid, nodedata, nodeindex) " +
197
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
198
              }
199
          } else {
200
              pstmt = connection.prepareStatement(
201
                  "INSERT INTO xml_nodes " +
202
                  "(nodetype, nodename, nodeprefix, docid, " +
203
                  "rootnodeid, parentnodeid, nodedata, nodeindex) " +
204
                  "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
205
          }
206
        // Increase DBConnection usage count
207
        connection.increaseUsageCount(1);
208
      }
209

    
210
      // Bind the values to the query
211
      pstmt.setString(1, nodetype);
212
      int idx;
213
      if ( nodename != null && (idx = nodename.indexOf(":")) != -1 ) {
214
        pstmt.setString(2, nodename.substring(idx+1));
215
        pstmt.setString(3, nodename.substring(0,idx));
216
      } else {
217
        pstmt.setString(2, nodename);
218
        pstmt.setString(3, null);
219
      }
220
      pstmt.setString(4, docid);
221
      if (nodetype != "DOCUMENT") {
222
        if (nodetype == "ELEMENT") {
223
          pstmt.setLong(5, getRootNodeID());
224
          pstmt.setLong(6, getParentID());
225
          pstmt.setString(7, data);
226
          pstmt.setInt(8, getNodeIndex());
227
        } else {
228
          pstmt.setLong(5, getRootNodeID());
229
          pstmt.setLong(6, getNodeID());
230
          pstmt.setString(7, data);
231
          pstmt.setInt(8, incChildNum());
232
        }
233
      }
234
      // Do the insertion
235
      logMetacat.debug("DBSAXNode.writeChildNodeToDBDataLimited - SQL insert: " + pstmt.toString());
236
      pstmt.execute();
237
      pstmt.close();
238

    
239
      // get the generated unique id afterward
240
      nid = DatabaseService.getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
241
      //should increase connection usage!!!!!!
242

    
243

    
244
      if (nodetype.equals("DOCUMENT")) {
245
        // Record the root node id that was generated from the database
246
        setRootNodeID(nid);
247
      }
248

    
249
      if (nodetype.equals("DOCUMENT") || nodetype.equals("ELEMENT")) {
250

    
251
        // Record the node id that was generated from the database
252
        setNodeID(nid);
253

    
254
        // Record the node type that was passed to the method
255
        setNodeType(nodetype);
256

    
257
      }
258

    
259
    } catch (SQLException sqle) {
260
      logMetacat.error("DBSAXNode.writeChildNodeToDBDataLimited - SQL error inserting node: " + 
261
    		  "(" + nodetype + ", " + nodename + ", " + data + ") : " + sqle.getMessage());
262
      sqle.printStackTrace(System.err);
263
      throw new SAXException(sqle.getMessage());
264
    }
265
    return nid;
266
  }
267

    
268
  /**
269
   * update rootnodeid=nodeid for 'DOCUMENT' type of nodes only
270
   */
271
  public void updateRootNodeID(long nodeid) throws SAXException {
272
      try {
273
        PreparedStatement pstmt;
274
        pstmt = connection.prepareStatement(
275
              "UPDATE xml_nodes set rootnodeid = ? " +
276
              "WHERE nodeid = ?");
277
        // Increase DBConnection usage count
278
        connection.increaseUsageCount(1);
279

    
280
        // Bind the values to the query
281
        pstmt.setLong(1, nodeid);
282
        pstmt.setLong(2, nodeid);
283
        // Do the update
284
        pstmt.execute();
285
        pstmt.close();
286
      } catch (SQLException e) {
287
        System.out.println("Error in DBSaxNode.updateRootNodeID: " +
288
                           e.getMessage());
289
        throw new SAXException(e.getMessage());
290
      }
291
  }
292

    
293
  /**
294
   * creates SQL code to put nodename for the document node
295
   * into DB connection
296
   */
297
  public void writeNodename(String nodename) throws SAXException {
298
      try {
299
        PreparedStatement pstmt;
300
        pstmt = connection.prepareStatement(
301
              "UPDATE xml_nodes set nodename = ? " +
302
              "WHERE nodeid = ?");
303
        // Increase DBConnection usage count
304
        connection.increaseUsageCount(1);
305

    
306
        // Bind the values to the query
307
        pstmt.setString(1, nodename);
308
        pstmt.setLong(2, getNodeID());
309
        // Do the insertion
310
        pstmt.execute();
311
        pstmt.close();
312
      } catch (SQLException e) {
313
        System.out.println("Error in DBSaxNode.writeNodeName: " +
314
                           e.getMessage());
315
        throw new SAXException(e.getMessage());
316
      }
317
  }
318

    
319
 /** creates SQL code and inserts new node into DB connection */
320
  public long writeDTDNodeToDB(String nodename, String data, String docid)
321
                                 throws SAXException
322
  {
323
    long nid = -1;
324
    try
325
    {
326

    
327
      PreparedStatement pstmt;
328
      logMetacat.info("DBSAXNode.writeDTDNodeToDB - Insert dtd into db: "+nodename +" "+data);
329
      if(data != null && !data.trim().equals("")){
330
            try{
331
                double numberData = Double.parseDouble(data);
332
                pstmt = connection.prepareStatement(
333
                    "INSERT INTO xml_nodes " +
334
                    "(nodetype, nodename, docid, " +
335
                    "rootnodeid, parentnodeid, nodedata, nodeindex, nodedatanumerical) " +
336
                    "VALUES (?, ?, ?, ?, ?, ?, ?, "+ numberData +")");
337
            } catch (NumberFormatException nfe) {
338
                pstmt = connection.prepareStatement(
339
                    "INSERT INTO xml_nodes " +
340
                    "(nodetype, nodename,  docid, " +
341
                    "rootnodeid, parentnodeid, nodedata, nodeindex) " +
342
                    "VALUES (?, ?, ?, ?, ?, ?, ?)");
343
            }
344
        } else {
345
            pstmt = connection.prepareStatement(
346
                  "INSERT INTO xml_nodes " +
347
                  "(nodetype, nodename, docid, " +
348
                  "rootnodeid, parentnodeid, nodedata, nodeindex) " +
349
                  "VALUES (?, ?, ?, ?, ?, ?, ?)");
350
        }
351

    
352
       // Increase DBConnection usage count
353
      connection.increaseUsageCount(1);
354

    
355
      // Bind the values to the query
356
      pstmt.setString(1, DocumentImpl.DTD);
357
      pstmt.setString(2, nodename);
358
      pstmt.setString(3, docid);
359
      pstmt.setLong(4, getRootNodeID());
360
      pstmt.setLong(5, getParentID());
361
      pstmt.setString(6, data);
362
      pstmt.setInt(7, getNodeIndex());
363

    
364
      // Do the insertion
365
      pstmt.execute();
366
      pstmt.close();
367

    
368
      // get the generated unique id afterward
369
      nid = DatabaseService.getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
370
      //should incease connection usage!!!!!!
371

    
372
    } catch (SQLException e) {
373
      System.out.println("Error in DBSaxNode.writeDTDNodeToDB");
374
      System.err.println("Error inserting node: (" + DocumentImpl.DTD + ", " +
375
                                                     nodename + ", " +
376
                                                     data + ")" );
377
      System.err.println(e.getMessage());
378
      e.printStackTrace(System.err);
379
      throw new SAXException(e.getMessage());
380
    }
381
    return nid;
382
  }
383

    
384

    
385
  /** get next node id from DB connection */
386
  private long generateNodeID() throws SAXException {
387
      long nid=0;
388
      Statement stmt;
389
      DBConnection dbConn = null;
390
      int serialNumber = -1;
391
      try {
392
        // Get DBConnection
393
        dbConn=DBConnectionPool.getDBConnection("DBSAXNode.generateNodeID");
394
        serialNumber=dbConn.getCheckOutSerialNumber();
395
        stmt = dbConn.createStatement();
396
        stmt.execute("SELECT xml_nodes_id_seq.nextval FROM dual");
397
        ResultSet rs = stmt.getResultSet();
398
        boolean tableHasRows = rs.next();
399
        if (tableHasRows) {
400
          nid = rs.getLong(1);
401
        }
402
        stmt.close();
403
      } catch (SQLException e) {
404
        System.out.println("Error in DBSaxNode.generateNodeID: " +
405
                            e.getMessage());
406
        throw new SAXException(e.getMessage());
407
      }
408
      finally
409
      {
410
        // Return DBconnection
411
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
412
      }//finally
413

    
414
      return nid;
415
  }
416

    
417
  /** Add a new attribute to this node, or set its value */
418
  public long setAttribute(String attName, String attValue, String docid)
419
              throws SAXException
420
  {
421
    long nodeId = -1;
422
    if (attName != null)
423
    {
424
      // Enter the attribute in the hash table
425
      super.setAttribute(attName, attValue);
426

    
427
      // And enter the attribute in the database
428
      nodeId = writeChildNodeToDB("ATTRIBUTE", attName, attValue, docid);
429
    }
430
    else
431
    {
432
      System.err.println("Attribute name must not be null!");
433
      throw new SAXException("Attribute name must not be null!");
434
    }
435
    return nodeId;
436
  }
437

    
438
  /** Add a namespace to this node */
439
  public long setNamespace(String prefix, String uri, String docid)
440
              throws SAXException
441
  {
442
    long nodeId = -1;
443
    if (prefix != null)
444
    {
445
      // Enter the namespace in a hash table
446
      super.setNamespace(prefix, uri);
447
      // And enter the namespace in the database
448
      nodeId = writeChildNodeToDB("NAMESPACE", prefix, uri, docid);
449
    }
450
    else
451
    {
452
      System.err.println("Namespace prefix must not be null!");
453
      throw new SAXException("Namespace prefix must not be null!");
454
    }
455
    return nodeId;
456
  }
457

    
458

    
459

    
460
  /**
461
   * USED FROM SEPARATE THREAD RUNNED from DBSAXHandler on endDocument()
462
   * Update the node index (xml_index) for this node by generating
463
   * test strings that represent all of the relative and absolute
464
   * paths through the XML tree from document root to this node
465
   */
466
  public void updateNodeIndex(DBConnection conn, String docid, String doctype)
467
               throws SAXException
468
  {
469
    Hashtable pathlist = new Hashtable();
470
    boolean atStartingNode = true;
471
    boolean atRootDocumentNode = false;
472
    DBSAXNode nodePointer = this;
473
    StringBuffer currentPath = new StringBuffer();
474
    int counter = 0;
475

    
476
    // Create a Hashtable of all of the paths to reach this node
477
    // including absolute paths and relative paths
478
    while (!atRootDocumentNode) {
479
      if (atStartingNode) {
480
        currentPath.insert(0, nodePointer.getTagName());
481
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
482
        counter++;
483
        atStartingNode = false;
484
      } else {
485
        currentPath.insert(0, "/");
486
        currentPath.insert(0, nodePointer.getTagName());
487
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
488
        counter++;
489
      }
490

    
491
      // advance to the next parent node
492
      nodePointer = nodePointer.getParentNode();
493

    
494
      // If we're at the DOCUMENT node (root of DOM tree), add
495
      // the root "/" to make the absolute path
496
      if (nodePointer.getNodeType().equals("DOCUMENT")) {
497
        currentPath.insert(0, "/");
498
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
499
        counter++;
500
        atRootDocumentNode = true;
501
      }
502
    }
503

    
504
    try {
505
      // Create an insert statement to reuse for all of the path insertions
506
      PreparedStatement pstmt = conn.prepareStatement(
507
              "INSERT INTO xml_index (nodeid, path, docid, doctype, " +
508
               "parentnodeid) " +
509
              "VALUES (?, ?, ?, ?, ?)");
510
      // Increase usage count
511
      conn.increaseUsageCount(1);
512

    
513
      pstmt.setString(3, docid);
514
      pstmt.setString(4, doctype);
515
      pstmt.setLong(5, getParentID());
516

    
517
      // Step through the hashtable and insert each of the path values
518
      Enumeration en = pathlist.keys();
519
      while (en.hasMoreElements()) {
520
        String path = (String)en.nextElement();
521
        Long nodeid = (Long)pathlist.get(path);
522
        pstmt.setLong(1, nodeid.longValue());
523
        pstmt.setString(2, path);
524

    
525
        pstmt.executeUpdate();
526
      }
527
      // Close the database statement
528
      pstmt.close();
529
    } catch (SQLException sqe) {
530
      System.err.println("SQL Exception while inserting path to index in " +
531
                         "DBSAXNode.updateNodeIndex for document " + docid);
532
      System.err.println(sqe.getMessage());
533
      throw new SAXException(sqe.getMessage());
534
    }
535
  }
536

    
537
  /** get the parent of this node */
538
  public DBSAXNode getParentNode() {
539
    return parentNode;
540
  }
541
}
(19-19/61)