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
 *    Release: @release@
8
 *
9
 *   '$Author: tao $'
10
 *     '$Date: 2003-03-19 15:22:19 -0800 (Wed, 19 Mar 2003) $'
11
 * '$Revision: 1495 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat;
29

    
30
import java.sql.*;
31
import java.io.IOException;
32
import java.util.Hashtable;
33
import java.util.Enumeration;
34
//import oracle.jdbc.driver.*;
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

    
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
  /** creates SQL code and inserts new node into DB connection */
88
  public long writeChildNodeToDB(String nodetype, String nodename,
89
                                 String data, String docid) 
90
                                 throws SAXException 
91
  {
92
    long nid = -1;
93
    try 
94
    {
95
 
96
      PreparedStatement pstmt;
97
      if (nodetype == "DOCUMENT") {
98
        pstmt = connection.prepareStatement(
99
            "INSERT INTO xml_nodes " +
100
            "(nodetype, nodename, nodeprefix, docid) " +
101
            "VALUES (?, ?, ?, ?)");
102
        
103
        // Increase DBConnection usage count
104
        connection.increaseUsageCount(1);
105
        MetaCatUtil.debugMessage("INSERTING DOCNAME: " + nodename, 35);
106
      } else {
107
        pstmt = connection.prepareStatement(
108
            "INSERT INTO xml_nodes " +
109
            "(nodetype, nodename, nodeprefix, docid, " +
110
            "rootnodeid, parentnodeid, nodedata, nodeindex) " +
111
            "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
112
        // Increase DBConnection usage count
113
        connection.increaseUsageCount(1);
114
      }
115

    
116
      // Bind the values to the query
117
      pstmt.setString(1, nodetype);
118
      int idx;
119
      if ( nodename != null && (idx = nodename.indexOf(":")) != -1 ) {
120
        pstmt.setString(2, nodename.substring(idx+1));
121
        pstmt.setString(3, nodename.substring(0,idx));
122
      } else {
123
        pstmt.setString(2, nodename);
124
        pstmt.setString(3, null);
125
      }
126
      pstmt.setString(4, docid);
127
      if (nodetype == "DOCUMENT") {
128
        // moved it to separate method updateRootNodeID
129
        //pstmt.setLong(4, nid);
130
      } else {
131
        if (nodetype == "ELEMENT") {
132
          pstmt.setLong(5, getRootNodeID());
133
          pstmt.setLong(6, getParentID());
134
          pstmt.setString(7, data);
135
          pstmt.setInt(8, getNodeIndex());
136
        } else {
137
          pstmt.setLong(5, getRootNodeID());
138
          pstmt.setLong(6, getNodeID());
139
          pstmt.setString(7, data);
140
          pstmt.setInt(8, incChildNum());
141
        }
142
      }
143
      // Do the insertion
144
      pstmt.execute();
145
      pstmt.close();
146

    
147
      // get the generated unique id afterward
148
      nid = dbAdapter.getUniqueID(connection.getConnections(), "xml_nodes");
149
      //should incease connection usage!!!!!!
150
     
151
      
152
      if (nodetype.equals("DOCUMENT")) {
153
        // Record the root node id that was generated from the database
154
        setRootNodeID(nid);
155
      }
156

    
157
      if (nodetype.equals("DOCUMENT") || nodetype.equals("ELEMENT")) {
158

    
159
        // Record the node id that was generated from the database
160
        setNodeID(nid);
161

    
162
        // Record the node type that was passed to the method
163
        setNodeType(nodetype);
164

    
165
      }
166

    
167
    } catch (SQLException e) {
168
      System.out.println("Error in DBSaxNode.writeChildNodeToDB");
169
      System.err.println("Error inserting node: (" + nodetype + ", " +
170
                                                     nodename + ", " + 
171
                                                     data + ")" );
172
      System.err.println(e.getMessage());
173
      e.printStackTrace(System.err);
174
      throw new SAXException(e.getMessage());
175
    }
176
    return nid;
177
  }
178

    
179
  /** 
180
   * update rootnodeid=nodeid for 'DOCUMENT' type of nodes only
181
   */
182
  public void updateRootNodeID(long nodeid) throws SAXException {
183
      try {
184
        PreparedStatement pstmt;
185
        pstmt = connection.prepareStatement(
186
              "UPDATE xml_nodes set rootnodeid = ? " +
187
              "WHERE nodeid = ?");
188
        // Increase DBConnection usage count
189
        connection.increaseUsageCount(1);
190

    
191
        // Bind the values to the query
192
        pstmt.setLong(1, nodeid);
193
        pstmt.setLong(2, nodeid);
194
        // Do the update
195
        pstmt.execute();
196
        pstmt.close();
197
      } catch (SQLException e) {
198
        System.out.println("Error in DBSaxNode.updateRootNodeID: " + 
199
                           e.getMessage());
200
        throw new SAXException(e.getMessage());
201
      }
202
  }
203

    
204
  /** 
205
   * creates SQL code to put nodename for the document node 
206
   * into DB connection 
207
   */
208
  public void writeNodename(String nodename) throws SAXException {
209
      try {
210
        PreparedStatement pstmt;
211
        pstmt = connection.prepareStatement(
212
              "UPDATE xml_nodes set nodename = ? " +
213
              "WHERE nodeid = ?");
214
        // Increase DBConnection usage count
215
        connection.increaseUsageCount(1);
216

    
217
        // Bind the values to the query
218
        pstmt.setString(1, nodename);
219
        pstmt.setLong(2, getNodeID());
220
        // Do the insertion
221
        pstmt.execute();
222
        pstmt.close();
223
      } catch (SQLException e) {
224
        System.out.println("Error in DBSaxNode.writeNodeName: " + 
225
                           e.getMessage());
226
        throw new SAXException(e.getMessage());
227
      }
228
  }
229

    
230
  /** get next node id from DB connection */
231
  private long generateNodeID() throws SAXException {
232
      long nid=0;
233
      Statement stmt;
234
      DBConnection dbConn = null;
235
      int serialNumber = -1;
236
      try {
237
        // Get DBConnection
238
        dbConn=DBConnectionPool.getDBConnection("DBSAXNode.generateNodeID");
239
        serialNumber=dbConn.getCheckOutSerialNumber();
240
        stmt = dbConn.createStatement();
241
        stmt.execute("SELECT xml_nodes_id_seq.nextval FROM dual");
242
        ResultSet rs = stmt.getResultSet();
243
        boolean tableHasRows = rs.next();
244
        if (tableHasRows) {
245
          nid = rs.getLong(1);
246
        }
247
        stmt.close();
248
      } catch (SQLException e) {
249
        System.out.println("Error in DBSaxNode.generateNodeID: " + 
250
                            e.getMessage());
251
        throw new SAXException(e.getMessage());
252
      }
253
      finally
254
      {
255
        // Return DBconnection
256
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
257
      }//finally
258

    
259
      return nid;
260
  }
261

    
262
  /** Add a new attribute to this node, or set its value */
263
  public long setAttribute(String attName, String attValue, String docid)
264
              throws SAXException 
265
  {
266
    long nodeId = -1;
267
    if (attName != null) 
268
    {
269
      // Enter the attribute in the hash table
270
      super.setAttribute(attName, attValue);
271

    
272
      // And enter the attribute in the database
273
      nodeId = writeChildNodeToDB("ATTRIBUTE", attName, attValue, docid);
274
    } 
275
    else 
276
    {
277
      System.err.println("Attribute name must not be null!");
278
      throw new SAXException("Attribute name must not be null!");
279
    }
280
    return nodeId;
281
  }
282

    
283
  /** Add a namespace to this node */
284
  public long setNamespace(String prefix, String uri, String docid)
285
              throws SAXException 
286
  {
287
    long nodeId = -1;
288
    if (prefix != null) 
289
    {
290
      // Enter the namespace in a hash table
291
      super.setNamespace(prefix, uri);
292
      // And enter the namespace in the database
293
      nodeId = writeChildNodeToDB("NAMESPACE", prefix, uri, docid);
294
    } 
295
    else 
296
    {
297
      System.err.println("Namespace prefix must not be null!");
298
      throw new SAXException("Namespace prefix must not be null!");
299
    }
300
    return nodeId;
301
  }
302

    
303
 
304

    
305
  /** 
306
   * USED FROM SEPARATE THREAD RUNNED from DBSAXHandler on endDocument()
307
   * Update the node index (xml_index) for this node by generating
308
   * test strings that represent all of the relative and absolute
309
   * paths through the XML tree from document root to this node
310
   */
311
  public void updateNodeIndex(DBConnection conn, String docid, String doctype) 
312
               throws SAXException
313
  {
314
    Hashtable pathlist = new Hashtable();
315
    boolean atStartingNode = true;
316
    boolean atRootDocumentNode = false;
317
    DBSAXNode nodePointer = this;
318
    StringBuffer currentPath = new StringBuffer();
319
    int counter = 0;
320

    
321
    // Create a Hashtable of all of the paths to reach this node
322
    // including absolute paths and relative paths
323
    while (!atRootDocumentNode) {
324
      if (atStartingNode) {
325
        currentPath.insert(0, nodePointer.getTagName());
326
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
327
        counter++;
328
        atStartingNode = false;
329
      } else {
330
        currentPath.insert(0, "/");
331
        currentPath.insert(0, nodePointer.getTagName());
332
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
333
        counter++;
334
      }
335

    
336
      // advance to the next parent node
337
      nodePointer = nodePointer.getParentNode();
338

    
339
      // If we're at the DOCUMENT node (root of DOM tree), add
340
      // the root "/" to make the absolute path
341
      if (nodePointer.getNodeType().equals("DOCUMENT")) {
342
        currentPath.insert(0, "/");
343
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
344
        counter++;
345
        atRootDocumentNode = true;
346
      } 
347
    }
348

    
349
    try {
350
      // Create an insert statement to reuse for all of the path insertions
351
      PreparedStatement pstmt = conn.prepareStatement(
352
              "INSERT INTO xml_index (nodeid, path, docid, doctype, " + 
353
               "parentnodeid) " + 
354
              "VALUES (?, ?, ?, ?, ?)");
355
      // Increase usage count
356
      conn.increaseUsageCount(1);
357
  
358
      pstmt.setString(3, docid);
359
      pstmt.setString(4, doctype);
360
      pstmt.setLong(5, getParentID());
361
      
362
      // Step through the hashtable and insert each of the path values
363
      Enumeration en = pathlist.keys();
364
      while (en.hasMoreElements()) {
365
        String path = (String)en.nextElement();
366
        Long nodeid = (Long)pathlist.get(path);
367
        pstmt.setLong(1, nodeid.longValue());
368
        pstmt.setString(2, path);
369
        
370
        pstmt.executeUpdate();
371
      }
372
      // Close the database statement
373
      pstmt.close();
374
    } catch (SQLException sqe) {
375
      System.err.println("SQL Exception while inserting path to index in " +
376
                         "DBSAXNode.updateNodeIndex for document " + docid);
377
      System.err.println(sqe.getMessage());
378
      throw new SAXException(sqe.getMessage());
379
    }
380
  }
381
 
382
  /** get the parent of this node */
383
  public DBSAXNode getParentNode() {
384
    return parentNode;
385
  }
386
}
(22-22/54)