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: jones $'
9
 *     '$Date: 2006-11-10 10:25:38 -0800 (Fri, 10 Nov 2006) $'
10
 * '$Revision: 3077 $'
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.io.IOException;
31
import java.util.Hashtable;
32
import java.util.Enumeration;
33
//import oracle.jdbc.driver.*;
34
import org.apache.log4j.Logger;
35
import org.xml.sax.SAXException;
36

    
37
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
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 static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
48
  private Logger logMetacat = Logger.getLogger(DBSAXNode.class);
49

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

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

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

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

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

    
102
    super();
103
    this.connection = conn;
104
    this.parentNode = parentNode;
105
    setParentID(parentNode.getNodeID());
106
    setRootNodeID(rootnodeid);
107
    setDocID(docid);
108
    // insert dtd node only for external dtd definiation
109
    if (systemId != null)
110
    {
111
      //write docname to DB
112
      if (docName != null)
113
      {
114
        setNodeIndex(parentNode.incChildNum());
115
        writeDTDNodeToDB(DocumentImpl.DOCNAME, docName, docid);
116
      }
117
      //write publicId to DB
118
      if (publicId != null)
119
      {
120
        setNodeIndex(parentNode.incChildNum());
121
        writeDTDNodeToDB(DocumentImpl.PUBLICID, publicId, docid);
122
      }
123
      //write systemId to DB
124
      setNodeIndex(parentNode.incChildNum());
125
      writeDTDNodeToDB(DocumentImpl.SYSTEMID, systemId, docid);
126
    }
127
  }
128

    
129
  /** creates SQL code and inserts new node into DB connection */
130
  public long writeChildNodeToDB(String nodetype, String nodename,
131
                                 String data, String docid)
132
                                 throws SAXException
133
  {
134
    long nid = -1;
135
    try
136
    {
137

    
138
      PreparedStatement pstmt;
139

    
140
      if (nodetype == "DOCUMENT") {
141
        pstmt = connection.prepareStatement(
142
            "INSERT INTO xml_nodes " +
143
            "(nodetype, nodename, nodeprefix, docid) " +
144
            "VALUES (?, ?, ?, ?)");
145

    
146
        // Increase DBConnection usage count
147
        connection.increaseUsageCount(1);
148
        logMetacat.info("INSERTING DOCNAME: " + nodename);
149
      } else {
150
          if(data != null && !data.trim().equals("")
151
             && !data.trim().equals("NaN")){
152
              try{
153
                  double numberData = Double.parseDouble(data);
154
                  pstmt = connection.prepareStatement(
155
                      "INSERT INTO xml_nodes " +
156
                      "(nodetype, nodename, nodeprefix, docid, " +
157
                      "rootnodeid, parentnodeid, nodedata, nodeindex, nodedatanumerical) " +
158
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, "+ numberData +")");
159
              } catch (NumberFormatException nfe) {
160
                  pstmt = connection.prepareStatement(
161
                      "INSERT INTO xml_nodes " +
162
                      "(nodetype, nodename, nodeprefix, docid, " +
163
                      "rootnodeid, parentnodeid, nodedata, nodeindex) " +
164
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
165
              }
166
          } else {
167
              pstmt = connection.prepareStatement(
168
                  "INSERT INTO xml_nodes " +
169
                  "(nodetype, nodename, nodeprefix, docid, " +
170
                  "rootnodeid, parentnodeid, nodedata, nodeindex) " +
171
                  "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
172
          }
173
        // Increase DBConnection usage count
174
        connection.increaseUsageCount(1);
175
      }
176

    
177
      // Bind the values to the query
178
      pstmt.setString(1, nodetype);
179
      int idx;
180
      if ( nodename != null && (idx = nodename.indexOf(":")) != -1 ) {
181
        pstmt.setString(2, nodename.substring(idx+1));
182
        pstmt.setString(3, nodename.substring(0,idx));
183
      } else {
184
        pstmt.setString(2, nodename);
185
        pstmt.setString(3, null);
186
      }
187
      pstmt.setString(4, docid);
188
      if (nodetype == "DOCUMENT") {
189
        // moved it to separate method updateRootNodeID
190
        //pstmt.setLong(4, nid);
191
      } else {
192
        if (nodetype == "ELEMENT") {
193
          pstmt.setLong(5, getRootNodeID());
194
          pstmt.setLong(6, getParentID());
195
          pstmt.setString(7, data);
196
          pstmt.setInt(8, getNodeIndex());
197
        } else {
198
          pstmt.setLong(5, getRootNodeID());
199
          pstmt.setLong(6, getNodeID());
200
          pstmt.setString(7, data);
201
          pstmt.setInt(8, incChildNum());
202
        }
203
      }
204
      // Do the insertion
205
      pstmt.execute();
206
      pstmt.close();
207

    
208
      // get the generated unique id afterward
209
      nid = dbAdapter.getUniqueID(connection.getConnections(), "xml_nodes");
210
      //should incease connection usage!!!!!!
211

    
212

    
213
      if (nodetype.equals("DOCUMENT")) {
214
        // Record the root node id that was generated from the database
215
        setRootNodeID(nid);
216
      }
217

    
218
      if (nodetype.equals("DOCUMENT") || nodetype.equals("ELEMENT")) {
219

    
220
        // Record the node id that was generated from the database
221
        setNodeID(nid);
222

    
223
        // Record the node type that was passed to the method
224
        setNodeType(nodetype);
225

    
226
      }
227

    
228
    } catch (SQLException e) {
229
      System.out.println("Error in DBSaxNode.writeChildNodeToDB");
230
      System.err.println("Error inserting node: (" + nodetype + ", " +
231
                                                     nodename + ", " +
232
                                                     data + ")" );
233
      System.err.println(e.getMessage());
234
      e.printStackTrace(System.err);
235
      throw new SAXException(e.getMessage());
236
    }
237
    return nid;
238
  }
239

    
240
  /**
241
   * update rootnodeid=nodeid for 'DOCUMENT' type of nodes only
242
   */
243
  public void updateRootNodeID(long nodeid) throws SAXException {
244
      try {
245
        PreparedStatement pstmt;
246
        pstmt = connection.prepareStatement(
247
              "UPDATE xml_nodes set rootnodeid = ? " +
248
              "WHERE nodeid = ?");
249
        // Increase DBConnection usage count
250
        connection.increaseUsageCount(1);
251

    
252
        // Bind the values to the query
253
        pstmt.setLong(1, nodeid);
254
        pstmt.setLong(2, nodeid);
255
        // Do the update
256
        pstmt.execute();
257
        pstmt.close();
258
      } catch (SQLException e) {
259
        System.out.println("Error in DBSaxNode.updateRootNodeID: " +
260
                           e.getMessage());
261
        throw new SAXException(e.getMessage());
262
      }
263
  }
264

    
265
  /**
266
   * creates SQL code to put nodename for the document node
267
   * into DB connection
268
   */
269
  public void writeNodename(String nodename) throws SAXException {
270
      try {
271
        PreparedStatement pstmt;
272
        pstmt = connection.prepareStatement(
273
              "UPDATE xml_nodes set nodename = ? " +
274
              "WHERE nodeid = ?");
275
        // Increase DBConnection usage count
276
        connection.increaseUsageCount(1);
277

    
278
        // Bind the values to the query
279
        pstmt.setString(1, nodename);
280
        pstmt.setLong(2, getNodeID());
281
        // Do the insertion
282
        pstmt.execute();
283
        pstmt.close();
284
      } catch (SQLException e) {
285
        System.out.println("Error in DBSaxNode.writeNodeName: " +
286
                           e.getMessage());
287
        throw new SAXException(e.getMessage());
288
      }
289
  }
290

    
291
 /** creates SQL code and inserts new node into DB connection */
292
  public long writeDTDNodeToDB(String nodename, String data, String docid)
293
                                 throws SAXException
294
  {
295
    long nid = -1;
296
    try
297
    {
298

    
299
      PreparedStatement pstmt;
300
      logMetacat.info("Insert dtd into db: "+nodename +" "+data);
301
      if(data != null && !data.trim().equals("")){
302
            try{
303
                double numberData = Double.parseDouble(data);
304
                pstmt = connection.prepareStatement(
305
                    "INSERT INTO xml_nodes " +
306
                    "(nodetype, nodename, docid, " +
307
                    "rootnodeid, parentnodeid, nodedata, nodeindex, nodedatanumerical) " +
308
                    "VALUES (?, ?, ?, ?, ?, ?, ?, "+ numberData +")");
309
            } catch (NumberFormatException nfe) {
310
                pstmt = connection.prepareStatement(
311
                    "INSERT INTO xml_nodes " +
312
                    "(nodetype, nodename,  docid, " +
313
                    "rootnodeid, parentnodeid, nodedata, nodeindex) " +
314
                    "VALUES (?, ?, ?, ?, ?, ?, ?)");
315
            }
316
        } else {
317
            pstmt = connection.prepareStatement(
318
                  "INSERT INTO xml_nodes " +
319
                  "(nodetype, nodename, docid, " +
320
                  "rootnodeid, parentnodeid, nodedata, nodeindex) " +
321
                  "VALUES (?, ?, ?, ?, ?, ?, ?)");
322
        }
323

    
324
       // Increase DBConnection usage count
325
      connection.increaseUsageCount(1);
326

    
327
      // Bind the values to the query
328
      pstmt.setString(1, DocumentImpl.DTD);
329
      pstmt.setString(2, nodename);
330
      pstmt.setString(3, docid);
331
      pstmt.setLong(4, getRootNodeID());
332
      pstmt.setLong(5, getParentID());
333
      pstmt.setString(6, data);
334
      pstmt.setInt(7, getNodeIndex());
335

    
336
      // Do the insertion
337
      pstmt.execute();
338
      pstmt.close();
339

    
340
      // get the generated unique id afterward
341
      nid = dbAdapter.getUniqueID(connection.getConnections(), "xml_nodes");
342
      //should incease connection usage!!!!!!
343

    
344
    } catch (SQLException e) {
345
      System.out.println("Error in DBSaxNode.writeDTDNodeToDB");
346
      System.err.println("Error inserting node: (" + DocumentImpl.DTD + ", " +
347
                                                     nodename + ", " +
348
                                                     data + ")" );
349
      System.err.println(e.getMessage());
350
      e.printStackTrace(System.err);
351
      throw new SAXException(e.getMessage());
352
    }
353
    return nid;
354
  }
355

    
356

    
357
  /** get next node id from DB connection */
358
  private long generateNodeID() throws SAXException {
359
      long nid=0;
360
      Statement stmt;
361
      DBConnection dbConn = null;
362
      int serialNumber = -1;
363
      try {
364
        // Get DBConnection
365
        dbConn=DBConnectionPool.getDBConnection("DBSAXNode.generateNodeID");
366
        serialNumber=dbConn.getCheckOutSerialNumber();
367
        stmt = dbConn.createStatement();
368
        stmt.execute("SELECT xml_nodes_id_seq.nextval FROM dual");
369
        ResultSet rs = stmt.getResultSet();
370
        boolean tableHasRows = rs.next();
371
        if (tableHasRows) {
372
          nid = rs.getLong(1);
373
        }
374
        stmt.close();
375
      } catch (SQLException e) {
376
        System.out.println("Error in DBSaxNode.generateNodeID: " +
377
                            e.getMessage());
378
        throw new SAXException(e.getMessage());
379
      }
380
      finally
381
      {
382
        // Return DBconnection
383
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
384
      }//finally
385

    
386
      return nid;
387
  }
388

    
389
  /** Add a new attribute to this node, or set its value */
390
  public long setAttribute(String attName, String attValue, String docid)
391
              throws SAXException
392
  {
393
    long nodeId = -1;
394
    if (attName != null)
395
    {
396
      // Enter the attribute in the hash table
397
      super.setAttribute(attName, attValue);
398

    
399
      // And enter the attribute in the database
400
      nodeId = writeChildNodeToDB("ATTRIBUTE", attName, attValue, docid);
401
    }
402
    else
403
    {
404
      System.err.println("Attribute name must not be null!");
405
      throw new SAXException("Attribute name must not be null!");
406
    }
407
    return nodeId;
408
  }
409

    
410
  /** Add a namespace to this node */
411
  public long setNamespace(String prefix, String uri, String docid)
412
              throws SAXException
413
  {
414
    long nodeId = -1;
415
    if (prefix != null)
416
    {
417
      // Enter the namespace in a hash table
418
      super.setNamespace(prefix, uri);
419
      // And enter the namespace in the database
420
      nodeId = writeChildNodeToDB("NAMESPACE", prefix, uri, docid);
421
    }
422
    else
423
    {
424
      System.err.println("Namespace prefix must not be null!");
425
      throw new SAXException("Namespace prefix must not be null!");
426
    }
427
    return nodeId;
428
  }
429

    
430

    
431

    
432
  /**
433
   * USED FROM SEPARATE THREAD RUNNED from DBSAXHandler on endDocument()
434
   * Update the node index (xml_index) for this node by generating
435
   * test strings that represent all of the relative and absolute
436
   * paths through the XML tree from document root to this node
437
   */
438
  public void updateNodeIndex(DBConnection conn, String docid, String doctype)
439
               throws SAXException
440
  {
441
    Hashtable pathlist = new Hashtable();
442
    boolean atStartingNode = true;
443
    boolean atRootDocumentNode = false;
444
    DBSAXNode nodePointer = this;
445
    StringBuffer currentPath = new StringBuffer();
446
    int counter = 0;
447

    
448
    // Create a Hashtable of all of the paths to reach this node
449
    // including absolute paths and relative paths
450
    while (!atRootDocumentNode) {
451
      if (atStartingNode) {
452
        currentPath.insert(0, nodePointer.getTagName());
453
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
454
        counter++;
455
        atStartingNode = false;
456
      } else {
457
        currentPath.insert(0, "/");
458
        currentPath.insert(0, nodePointer.getTagName());
459
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
460
        counter++;
461
      }
462

    
463
      // advance to the next parent node
464
      nodePointer = nodePointer.getParentNode();
465

    
466
      // If we're at the DOCUMENT node (root of DOM tree), add
467
      // the root "/" to make the absolute path
468
      if (nodePointer.getNodeType().equals("DOCUMENT")) {
469
        currentPath.insert(0, "/");
470
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
471
        counter++;
472
        atRootDocumentNode = true;
473
      }
474
    }
475

    
476
    try {
477
      // Create an insert statement to reuse for all of the path insertions
478
      PreparedStatement pstmt = conn.prepareStatement(
479
              "INSERT INTO xml_index (nodeid, path, docid, doctype, " +
480
               "parentnodeid) " +
481
              "VALUES (?, ?, ?, ?, ?)");
482
      // Increase usage count
483
      conn.increaseUsageCount(1);
484

    
485
      pstmt.setString(3, docid);
486
      pstmt.setString(4, doctype);
487
      pstmt.setLong(5, getParentID());
488

    
489
      // Step through the hashtable and insert each of the path values
490
      Enumeration en = pathlist.keys();
491
      while (en.hasMoreElements()) {
492
        String path = (String)en.nextElement();
493
        Long nodeid = (Long)pathlist.get(path);
494
        pstmt.setLong(1, nodeid.longValue());
495
        pstmt.setString(2, path);
496

    
497
        pstmt.executeUpdate();
498
      }
499
      // Close the database statement
500
      pstmt.close();
501
    } catch (SQLException sqe) {
502
      System.err.println("SQL Exception while inserting path to index in " +
503
                         "DBSAXNode.updateNodeIndex for document " + docid);
504
      System.err.println(sqe.getMessage());
505
      throw new SAXException(sqe.getMessage());
506
    }
507
  }
508

    
509
  /** get the parent of this node */
510
  public DBSAXNode getParentNode() {
511
    return parentNode;
512
  }
513
}
(23-23/65)