Project

General

Profile

1 15 jones
/**
2 203 jones
 *  '$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 15 jones
 *
8 203 jones
 *   '$Author$'
9
 *     '$Date$'
10
 * '$Revision$'
11 669 jones
 *
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 15 jones
 */
26
27 75 jones
package edu.ucsb.nceas.metacat;
28 15 jones
29
import java.sql.*;
30
import java.util.Hashtable;
31
import java.util.Enumeration;
32 2663 sgarg
import org.apache.log4j.Logger;
33 461 bojilova
import org.xml.sax.SAXException;
34 15 jones
35 5015 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
36
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
37
import edu.ucsb.nceas.metacat.database.DatabaseService;
38 747 bojilova
39 2358 sgarg
/**
40 137 jones
 * A Class that represents an XML node and its contents and
41 31 jones
 * can write its own representation to a database connection
42
 */
43 137 jones
public class DBSAXNode extends BasicNode {
44 15 jones
45 1217 tao
  private DBConnection	connection;
46 176 jones
  private DBSAXNode	parentNode;
47 2663 sgarg
  private Logger logMetacat = Logger.getLogger(DBSAXNode.class);
48 15 jones
49 2358 sgarg
  /**
50 136 jones
   * Construct a new node instance for DOCUMENT nodes
51 133 jones
   *
52
   * @param conn the JDBC Connection to which all information is written
53
   */
54 1217 tao
  public DBSAXNode (DBConnection conn, String docid) throws SAXException {
55 747 bojilova
56 408 jones
    super();
57 1217 tao
    this.connection = conn;
58 176 jones
    this.parentNode = null;
59 457 bojilova
    writeChildNodeToDB("DOCUMENT", null, null, docid);
60 758 bojilova
    updateRootNodeID(getNodeID());
61 136 jones
  }
62
63 2358 sgarg
  /**
64 136 jones
   * 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 1217 tao
  public DBSAXNode (DBConnection conn, String qName, String lName,
71 2358 sgarg
                    DBSAXNode parentNode, long rootnodeid,
72
                    String docid, String doctype)
73 826 bojilova
                                               throws SAXException {
74 21 jones
75 826 bojilova
    super(lName);
76 1217 tao
    this.connection = conn;
77 747 bojilova
    this.parentNode = parentNode;
78 136 jones
    setParentID(parentNode.getNodeID());
79 457 bojilova
    setRootNodeID(rootnodeid);
80
    setDocID(docid);
81 136 jones
    setNodeIndex(parentNode.incChildNum());
82 826 bojilova
    writeChildNodeToDB("ELEMENT", qName, null, docid);
83 471 bojilova
    //No writing XML Index from here. New Thread used instead.
84
    //updateNodeIndex(docid, doctype);
85 133 jones
  }
86 2358 sgarg
87
  /**
88 1803 tao
   * 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 2358 sgarg
  public DBSAXNode (DBConnection conn, String docName, String publicId,
97
                    String systemId, DBSAXNode parentNode, long rootnodeid,
98
                    String docid) throws SAXException
99 1803 tao
  {
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 5208 daigle
128 133 jones
  /** creates SQL code and inserts new node into DB connection */
129 1416 tao
  public long writeChildNodeToDB(String nodetype, String nodename,
130 2358 sgarg
                                 String data, String docid)
131 5208 daigle
                                 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 5311 daigle
        if (leftover > (DBSAXHandler.MAXDATACHARS)) {
148 5208 daigle
        	limitedData = data.substring(offset, DBSAXHandler.MAXDATACHARS);
149 5311 daigle
            leftover -= (DBSAXHandler.MAXDATACHARS - 1);
150
            offset += (DBSAXHandler.MAXDATACHARS - 1);
151 5208 daigle
        } else {
152 5311 daigle
        	if (data != null) {
153
        		limitedData = data.substring(offset, offset + leftover);
154
        	} else {
155
        		limitedData = null;
156
        	}
157
        	moredata = false;
158 5208 daigle
        }
159 5311 daigle
160
        endNodeId =  writeChildNodeToDBDataLimited(nodetype, nodename, limitedData, docid);
161 5208 daigle
    }
162
163
    return endNodeId;
164
  }
165
166
  /** creates SQL code and inserts new node into DB connection */
167
  public long writeChildNodeToDBDataLimited(String nodetype, String nodename,
168
                                 String data, String docid)
169 2358 sgarg
                                 throws SAXException
170 1416 tao
  {
171
    long nid = -1;
172 2358 sgarg
    try
173 1416 tao
    {
174 2358 sgarg
175 133 jones
      PreparedStatement pstmt;
176 2358 sgarg
177 133 jones
      if (nodetype == "DOCUMENT") {
178 1217 tao
        pstmt = connection.prepareStatement(
179 133 jones
            "INSERT INTO xml_nodes " +
180 826 bojilova
            "(nodetype, nodename, nodeprefix, docid) " +
181
            "VALUES (?, ?, ?, ?)");
182 2358 sgarg
183 1217 tao
        // Increase DBConnection usage count
184
        connection.increaseUsageCount(1);
185 5208 daigle
        logMetacat.debug("DBSAXNode.writeChildNodeToDBDataLimited - inserting doc name: " + nodename);
186 133 jones
      } else {
187 2364 sgarg
          if(data != null && !data.trim().equals("")
188 3197 tao
             && !data.trim().equals("NaN") && !data.trim().equalsIgnoreCase("Infinity")){
189 2358 sgarg
              try{
190
                  double numberData = Double.parseDouble(data);
191
                  pstmt = connection.prepareStatement(
192
                      "INSERT INTO xml_nodes " +
193
                      "(nodetype, nodename, nodeprefix, docid, " +
194
                      "rootnodeid, parentnodeid, nodedata, nodeindex, nodedatanumerical) " +
195
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, "+ numberData +")");
196
              } catch (NumberFormatException nfe) {
197
                  pstmt = connection.prepareStatement(
198
                      "INSERT INTO xml_nodes " +
199
                      "(nodetype, nodename, nodeprefix, docid, " +
200
                      "rootnodeid, parentnodeid, nodedata, nodeindex) " +
201
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
202
              }
203
          } else {
204
              pstmt = connection.prepareStatement(
205
                  "INSERT INTO xml_nodes " +
206
                  "(nodetype, nodename, nodeprefix, docid, " +
207
                  "rootnodeid, parentnodeid, nodedata, nodeindex) " +
208
                  "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
209
          }
210 1217 tao
        // Increase DBConnection usage count
211
        connection.increaseUsageCount(1);
212 133 jones
      }
213 19 jones
214 133 jones
      // Bind the values to the query
215 758 bojilova
      pstmt.setString(1, nodetype);
216 826 bojilova
      int idx;
217
      if ( nodename != null && (idx = nodename.indexOf(":")) != -1 ) {
218
        pstmt.setString(2, nodename.substring(idx+1));
219
        pstmt.setString(3, nodename.substring(0,idx));
220
      } else {
221
        pstmt.setString(2, nodename);
222
        pstmt.setString(3, null);
223
      }
224
      pstmt.setString(4, docid);
225 5208 daigle
      if (nodetype != "DOCUMENT") {
226 133 jones
        if (nodetype == "ELEMENT") {
227 826 bojilova
          pstmt.setLong(5, getRootNodeID());
228
          pstmt.setLong(6, getParentID());
229
          pstmt.setString(7, data);
230
          pstmt.setInt(8, getNodeIndex());
231 133 jones
        } else {
232 826 bojilova
          pstmt.setLong(5, getRootNodeID());
233
          pstmt.setLong(6, getNodeID());
234
          pstmt.setString(7, data);
235
          pstmt.setInt(8, incChildNum());
236 72 bojilova
        }
237 133 jones
      }
238
      // Do the insertion
239 5208 daigle
      logMetacat.debug("DBSAXNode.writeChildNodeToDBDataLimited - SQL insert: " + pstmt.toString());
240 133 jones
      pstmt.execute();
241
      pstmt.close();
242 758 bojilova
243
      // get the generated unique id afterward
244 5319 jones
      nid = DatabaseService.getInstance().getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
245 5208 daigle
      //should increase connection usage!!!!!!
246 2358 sgarg
247
248 408 jones
      if (nodetype.equals("DOCUMENT")) {
249
        // Record the root node id that was generated from the database
250
        setRootNodeID(nid);
251
      }
252
253 177 jones
      if (nodetype.equals("DOCUMENT") || nodetype.equals("ELEMENT")) {
254 176 jones
255 177 jones
        // Record the node id that was generated from the database
256
        setNodeID(nid);
257 176 jones
258 177 jones
        // Record the node type that was passed to the method
259
        setNodeType(nodetype);
260
261
      }
262
263 5208 daigle
    } catch (SQLException sqle) {
264
      logMetacat.error("DBSAXNode.writeChildNodeToDBDataLimited - SQL error inserting node: " +
265
    		  "(" + nodetype + ", " + nodename + ", " + data + ") : " + sqle.getMessage());
266
      sqle.printStackTrace(System.err);
267
      throw new SAXException(sqle.getMessage());
268 110 bojilova
    }
269 1416 tao
    return nid;
270 133 jones
  }
271 110 bojilova
272 2358 sgarg
  /**
273 758 bojilova
   * update rootnodeid=nodeid for 'DOCUMENT' type of nodes only
274
   */
275
  public void updateRootNodeID(long nodeid) throws SAXException {
276
      try {
277
        PreparedStatement pstmt;
278 1217 tao
        pstmt = connection.prepareStatement(
279 758 bojilova
              "UPDATE xml_nodes set rootnodeid = ? " +
280
              "WHERE nodeid = ?");
281 1217 tao
        // Increase DBConnection usage count
282
        connection.increaseUsageCount(1);
283 758 bojilova
284
        // Bind the values to the query
285
        pstmt.setLong(1, nodeid);
286
        pstmt.setLong(2, nodeid);
287
        // Do the update
288
        pstmt.execute();
289
        pstmt.close();
290
      } catch (SQLException e) {
291 2358 sgarg
        System.out.println("Error in DBSaxNode.updateRootNodeID: " +
292 758 bojilova
                           e.getMessage());
293
        throw new SAXException(e.getMessage());
294
      }
295
  }
296
297 2358 sgarg
  /**
298
   * creates SQL code to put nodename for the document node
299
   * into DB connection
300 133 jones
   */
301 461 bojilova
  public void writeNodename(String nodename) throws SAXException {
302 133 jones
      try {
303
        PreparedStatement pstmt;
304 1217 tao
        pstmt = connection.prepareStatement(
305 133 jones
              "UPDATE xml_nodes set nodename = ? " +
306
              "WHERE nodeid = ?");
307 1217 tao
        // Increase DBConnection usage count
308
        connection.increaseUsageCount(1);
309 16 jones
310 133 jones
        // Bind the values to the query
311
        pstmt.setString(1, nodename);
312 134 jones
        pstmt.setLong(2, getNodeID());
313 133 jones
        // Do the insertion
314
        pstmt.execute();
315
        pstmt.close();
316
      } catch (SQLException e) {
317 2358 sgarg
        System.out.println("Error in DBSaxNode.writeNodeName: " +
318 675 berkley
                           e.getMessage());
319 461 bojilova
        throw new SAXException(e.getMessage());
320 133 jones
      }
321
  }
322 16 jones
323 1803 tao
 /** creates SQL code and inserts new node into DB connection */
324 2358 sgarg
  public long writeDTDNodeToDB(String nodename, String data, String docid)
325
                                 throws SAXException
326 1803 tao
  {
327
    long nid = -1;
328 2358 sgarg
    try
329 1803 tao
    {
330 2358 sgarg
331 1803 tao
      PreparedStatement pstmt;
332 5208 daigle
      logMetacat.info("DBSAXNode.writeDTDNodeToDB - Insert dtd into db: "+nodename +" "+data);
333 2358 sgarg
      if(data != null && !data.trim().equals("")){
334
            try{
335
                double numberData = Double.parseDouble(data);
336
                pstmt = connection.prepareStatement(
337
                    "INSERT INTO xml_nodes " +
338
                    "(nodetype, nodename, docid, " +
339
                    "rootnodeid, parentnodeid, nodedata, nodeindex, nodedatanumerical) " +
340
                    "VALUES (?, ?, ?, ?, ?, ?, ?, "+ numberData +")");
341
            } catch (NumberFormatException nfe) {
342
                pstmt = connection.prepareStatement(
343
                    "INSERT INTO xml_nodes " +
344
                    "(nodetype, nodename,  docid, " +
345
                    "rootnodeid, parentnodeid, nodedata, nodeindex) " +
346
                    "VALUES (?, ?, ?, ?, ?, ?, ?)");
347
            }
348
        } else {
349
            pstmt = connection.prepareStatement(
350
                  "INSERT INTO xml_nodes " +
351
                  "(nodetype, nodename, docid, " +
352
                  "rootnodeid, parentnodeid, nodedata, nodeindex) " +
353
                  "VALUES (?, ?, ?, ?, ?, ?, ?)");
354
        }
355
356 1803 tao
       // Increase DBConnection usage count
357
      connection.increaseUsageCount(1);
358 2358 sgarg
359 1803 tao
      // Bind the values to the query
360
      pstmt.setString(1, DocumentImpl.DTD);
361
      pstmt.setString(2, nodename);
362
      pstmt.setString(3, docid);
363
      pstmt.setLong(4, getRootNodeID());
364
      pstmt.setLong(5, getParentID());
365
      pstmt.setString(6, data);
366
      pstmt.setInt(7, getNodeIndex());
367 2358 sgarg
368 1803 tao
      // Do the insertion
369
      pstmt.execute();
370
      pstmt.close();
371
372
      // get the generated unique id afterward
373 5319 jones
      nid = DatabaseService.getInstance().getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
374 1803 tao
      //should incease connection usage!!!!!!
375 2358 sgarg
376 1803 tao
    } catch (SQLException e) {
377
      System.out.println("Error in DBSaxNode.writeDTDNodeToDB");
378
      System.err.println("Error inserting node: (" + DocumentImpl.DTD + ", " +
379 2358 sgarg
                                                     nodename + ", " +
380 1803 tao
                                                     data + ")" );
381
      System.err.println(e.getMessage());
382
      e.printStackTrace(System.err);
383
      throw new SAXException(e.getMessage());
384
    }
385
    return nid;
386
  }
387 2358 sgarg
388
389 174 bojilova
  /** get next node id from DB connection */
390 461 bojilova
  private long generateNodeID() throws SAXException {
391 174 bojilova
      long nid=0;
392 133 jones
      Statement stmt;
393 1217 tao
      DBConnection dbConn = null;
394
      int serialNumber = -1;
395 133 jones
      try {
396 1217 tao
        // Get DBConnection
397
        dbConn=DBConnectionPool.getDBConnection("DBSAXNode.generateNodeID");
398
        serialNumber=dbConn.getCheckOutSerialNumber();
399
        stmt = dbConn.createStatement();
400 174 bojilova
        stmt.execute("SELECT xml_nodes_id_seq.nextval FROM dual");
401 133 jones
        ResultSet rs = stmt.getResultSet();
402
        boolean tableHasRows = rs.next();
403
        if (tableHasRows) {
404 174 bojilova
          nid = rs.getLong(1);
405 19 jones
        }
406 133 jones
        stmt.close();
407
      } catch (SQLException e) {
408 2358 sgarg
        System.out.println("Error in DBSaxNode.generateNodeID: " +
409 675 berkley
                            e.getMessage());
410 461 bojilova
        throw new SAXException(e.getMessage());
411 15 jones
      }
412 1217 tao
      finally
413
      {
414
        // Return DBconnection
415
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
416
      }//finally
417 15 jones
418 174 bojilova
      return nid;
419 133 jones
  }
420 18 jones
421 137 jones
  /** Add a new attribute to this node, or set its value */
422 1416 tao
  public long setAttribute(String attName, String attValue, String docid)
423 2358 sgarg
              throws SAXException
424 1416 tao
  {
425
    long nodeId = -1;
426 2358 sgarg
    if (attName != null)
427 1416 tao
    {
428 133 jones
      // Enter the attribute in the hash table
429
      super.setAttribute(attName, attValue);
430 18 jones
431 133 jones
      // And enter the attribute in the database
432 1416 tao
      nodeId = writeChildNodeToDB("ATTRIBUTE", attName, attValue, docid);
433 2358 sgarg
    }
434
    else
435 1416 tao
    {
436 133 jones
      System.err.println("Attribute name must not be null!");
437 461 bojilova
      throw new SAXException("Attribute name must not be null!");
438 18 jones
    }
439 1416 tao
    return nodeId;
440 133 jones
  }
441 176 jones
442 821 bojilova
  /** Add a namespace to this node */
443 1416 tao
  public long setNamespace(String prefix, String uri, String docid)
444 2358 sgarg
              throws SAXException
445 1416 tao
  {
446
    long nodeId = -1;
447 2358 sgarg
    if (prefix != null)
448 1416 tao
    {
449 821 bojilova
      // Enter the namespace in a hash table
450
      super.setNamespace(prefix, uri);
451
      // And enter the namespace in the database
452 1416 tao
      nodeId = writeChildNodeToDB("NAMESPACE", prefix, uri, docid);
453 2358 sgarg
    }
454
    else
455 1416 tao
    {
456 821 bojilova
      System.err.println("Namespace prefix must not be null!");
457
      throw new SAXException("Namespace prefix must not be null!");
458
    }
459 1416 tao
    return nodeId;
460 821 bojilova
  }
461
462 176 jones
463 2358 sgarg
464
  /**
465 471 bojilova
   * USED FROM SEPARATE THREAD RUNNED from DBSAXHandler on endDocument()
466
   * Update the node index (xml_index) for this node by generating
467
   * test strings that represent all of the relative and absolute
468
   * paths through the XML tree from document root to this node
469
   */
470 2358 sgarg
  public void updateNodeIndex(DBConnection conn, String docid, String doctype)
471 471 bojilova
               throws SAXException
472
  {
473
    Hashtable pathlist = new Hashtable();
474
    boolean atStartingNode = true;
475
    boolean atRootDocumentNode = false;
476
    DBSAXNode nodePointer = this;
477
    StringBuffer currentPath = new StringBuffer();
478
    int counter = 0;
479
480
    // Create a Hashtable of all of the paths to reach this node
481
    // including absolute paths and relative paths
482
    while (!atRootDocumentNode) {
483
      if (atStartingNode) {
484
        currentPath.insert(0, nodePointer.getTagName());
485
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
486
        counter++;
487
        atStartingNode = false;
488
      } else {
489
        currentPath.insert(0, "/");
490
        currentPath.insert(0, nodePointer.getTagName());
491
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
492
        counter++;
493
      }
494
495
      // advance to the next parent node
496
      nodePointer = nodePointer.getParentNode();
497
498
      // If we're at the DOCUMENT node (root of DOM tree), add
499
      // the root "/" to make the absolute path
500
      if (nodePointer.getNodeType().equals("DOCUMENT")) {
501
        currentPath.insert(0, "/");
502
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
503
        counter++;
504
        atRootDocumentNode = true;
505 2358 sgarg
      }
506 471 bojilova
    }
507
508
    try {
509
      // Create an insert statement to reuse for all of the path insertions
510
      PreparedStatement pstmt = conn.prepareStatement(
511 2358 sgarg
              "INSERT INTO xml_index (nodeid, path, docid, doctype, " +
512
               "parentnodeid) " +
513 471 bojilova
              "VALUES (?, ?, ?, ?, ?)");
514 1217 tao
      // Increase usage count
515
      conn.increaseUsageCount(1);
516 2358 sgarg
517 471 bojilova
      pstmt.setString(3, docid);
518
      pstmt.setString(4, doctype);
519
      pstmt.setLong(5, getParentID());
520 2358 sgarg
521 471 bojilova
      // Step through the hashtable and insert each of the path values
522 176 jones
      Enumeration en = pathlist.keys();
523
      while (en.hasMoreElements()) {
524
        String path = (String)en.nextElement();
525
        Long nodeid = (Long)pathlist.get(path);
526
        pstmt.setLong(1, nodeid.longValue());
527
        pstmt.setString(2, path);
528 2358 sgarg
529 457 bojilova
        pstmt.executeUpdate();
530 176 jones
      }
531
      // Close the database statement
532
      pstmt.close();
533
    } catch (SQLException sqe) {
534 675 berkley
      System.err.println("SQL Exception while inserting path to index in " +
535 899 berkley
                         "DBSAXNode.updateNodeIndex for document " + docid);
536 176 jones
      System.err.println(sqe.getMessage());
537 461 bojilova
      throw new SAXException(sqe.getMessage());
538 176 jones
    }
539
  }
540 2358 sgarg
541 176 jones
  /** get the parent of this node */
542
  public DBSAXNode getParentNode() {
543
    return parentNode;
544
  }
545 15 jones
}