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: 2008-08-29 10:20:19 -0700 (Fri, 29 Aug 2008) $'
10
 * '$Revision: 4335 $'
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.service.DatabaseService;
36

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

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

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

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

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

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

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

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

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

    
135
      PreparedStatement pstmt;
136

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

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

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

    
205
      // get the generated unique id afterward
206
      nid = DatabaseService.getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
207
      //should incease connection usage!!!!!!
208

    
209

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

    
215
      if (nodetype.equals("DOCUMENT") || nodetype.equals("ELEMENT")) {
216

    
217
        // Record the node id that was generated from the database
218
        setNodeID(nid);
219

    
220
        // Record the node type that was passed to the method
221
        setNodeType(nodetype);
222

    
223
      }
224

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

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

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

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

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

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

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

    
321
       // Increase DBConnection usage count
322
      connection.increaseUsageCount(1);
323

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

    
333
      // Do the insertion
334
      pstmt.execute();
335
      pstmt.close();
336

    
337
      // get the generated unique id afterward
338
      nid = DatabaseService.getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
339
      //should incease connection usage!!!!!!
340

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

    
353

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

    
383
      return nid;
384
  }
385

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

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

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

    
427

    
428

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

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

    
460
      // advance to the next parent node
461
      nodePointer = nodePointer.getParentNode();
462

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

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

    
482
      pstmt.setString(3, docid);
483
      pstmt.setString(4, doctype);
484
      pstmt.setLong(5, getParentID());
485

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

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

    
506
  /** get the parent of this node */
507
  public DBSAXNode getParentNode() {
508
    return parentNode;
509
  }
510
}
(22-22/67)