/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xalan.lib.sql;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.util.Vector;
import java.util.Enumeration;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.sql.Time;
import java.sql.Date;
import java.lang.Integer;
import java.lang.Double;
import org.apache.xpath.axes.ContextNodeList;
import org.apache.xalan.res.XSLTErrorResources;
/**
* <p>
* Represents a JDBC query statement. Also acts as both a
* NodeIterator and the Document node for the row-set representation
* of the query result set.
* </p>
*
*/
public class XStatement extends StreamableNode
implements NodeIterator, ContextNodeList, Cloneable
{
/** Flag for DEBUG mode */
private static final boolean DEBUG = false;
/**
* Ths JDBC Statement that is used for the query.
* It is allocated as a prepared statement but may
* only be use as a regular statemwnt.
*
*/
private Statement m_statement;
private PreparedStatement m_pstatement;
/** Node counter */
private int m_nodeCounter = 0;
private boolean m_ShouldCacheNodes = true;
/**
* Get And Increment Node Counter
*
*
* @return Node counter
*/
int getAndIncrementNodeCounter()
{
int c = m_nodeCounter;
m_nodeCounter++;
return c;
}
/**
* Get the JDBC Query statement
*
*
* @return the JDBC Query statement
*/
public Statement getStatement()
{
return m_statement;
}
/** ResultSet instance from executing the query string */
ResultSet m_resultSet;
/**
* Get the ResultSet from executing the query string
*
*
* @return ResultSet instance
*/
public ResultSet getResultSet()
{
return m_resultSet;
}
/** XConnection instance (for access to JDBC data). */
private XConnection m_xconnection;
/** The SQL Query string */
private String m_queryString;
/** Rowset instance (for the JDBC query result set). */
RowSet m_rowset;
/** Flag */
boolean m_nextHasBeenCalled = false;
/** Constant for DOCELEMENTNAME */
static final String S_DOCELEMENTNAME = "row-set";
/** Constant for COLUMNHEADERNAME */
static final String S_COLUMNHEADERNAME = "column-header";
/** Constant for ROWNAME */
static final String S_ROWNAME = "row";
/** Constant for COLUMNNAME */
static final String S_COLUMNNAME = "col";
/**
* The {@link org.apache.xalan.lib.sql.XConnection#query(java.lang.String) XConnection query()}
* method uses this constructor to execute a SQL query statement. When instantiated,
* XStatement executes the query and creates a
* {@link org.apache.xalan.lib.sql.RowSet RowSet}, a row-set element associated with the query
* result set.
* @param connection the XConnection object that calls this constructor.
* @param queryString the SQL query.
*
*
* @throws SQLException
*/
public XStatement(
XConnection connection,
String queryString,
boolean shouldCacheNodes)
throws SQLException
{
super(null);
if (DEBUG)
{
System.out.println("In XStatement constructor");
System.out.println("Query = " + queryString);
}
m_ShouldCacheNodes = shouldCacheNodes;
// The SQL statement which lets us execute commands against the connection.
m_xconnection = connection;
m_statement = m_xconnection.m_connection.createStatement();
m_queryString = queryString;
m_resultSet = m_statement.executeQuery(m_queryString);
m_rowset = new RowSet(this);
if (DEBUG)
System.out.println("Exiting XStatement constructor");
}
public XStatement(
XConnection connection,
String queryString,
Vector pList,
boolean shouldCacheNodes)
throws SQLException
{
super(null);
if (DEBUG)
System.out.println("In XStatement constructor for pquery");
m_ShouldCacheNodes = shouldCacheNodes;
// The SQL statement which lets us execute commands against the connection.
m_xconnection = connection;
m_queryString = queryString;
if (DEBUG)
{
System.out.println("Executing PQuery: " + m_queryString);
}
m_pstatement = m_xconnection.m_connection.prepareStatement(m_queryString);
Enumeration enum = pList.elements();
int indx = 1;
while (enum.hasMoreElements())
{
QueryParameter param = (QueryParameter) enum.nextElement();
setParameter(indx, m_pstatement, param);
indx++;
}
m_resultSet = m_pstatement.executeQuery();
m_rowset = new RowSet(this);
//
// Make a copy of the statement for external access
m_statement = m_pstatement;
if (DEBUG)
System.out.println("Exiting XStatement constructor");
}
/**
* Set the parameter for a Prepared Statement
* @param int pos, the Position of the place holder in the prepared statement
* @param PreparedStatement the current pquery being worked on
* @param QueryParameter the Vaue and type of that value to be added.
*
* @throws SQLException - will be converted to an ext-error
*
*/
public void setParameter(int pos, PreparedStatement stmt, QueryParameter p)
throws SQLException
{
String type = p.getType();
if (type.equalsIgnoreCase("string"))
{
stmt.setString(pos, p.getValue());
}
if (type.equalsIgnoreCase("bigdecimal"))
{
stmt.setBigDecimal(pos, new BigDecimal(p.getValue()));
}
if (type.equalsIgnoreCase("boolean"))
{
Integer i = new Integer( p.getValue() );
boolean b = ((i.intValue() != 0) ? false : true);
stmt.setBoolean(pos, b);
}
if (type.equalsIgnoreCase("bytes"))
{
stmt.setBytes(pos, p.getValue().getBytes());
}
if (type.equalsIgnoreCase("date"))
{
stmt.setDate(pos, Date.valueOf(p.getValue()));
}
if (type.equalsIgnoreCase("double"))
{
Double d = new Double(p.getValue());
stmt.setDouble(pos, d.doubleValue() );
}
if (type.equalsIgnoreCase("float"))
{
Float f = new Float(p.getValue());
stmt.setFloat(pos, f.floatValue());
}
if (type.equalsIgnoreCase("long"))
{
Long l = new Long(p.getValue());
stmt.setLong(pos, l.longValue());
}
if (type.equalsIgnoreCase("short"))
{
Short s = new Short(p.getValue());
stmt.setShort(pos, s.shortValue());
}
if (type.equalsIgnoreCase("time"))
{
stmt.setTime(pos, Time.valueOf(p.getValue()) );
}
if (type.equalsIgnoreCase("timestamp"))
{
stmt.setTimestamp(pos, Timestamp.valueOf(p.getValue()) );
}
}
/**
* Get the representation of the JDBC Query statement
*
*
* @return the representation of the JDBC Query statement, this
*/
public XStatement getXStatement()
{
return this;
}
// ============== NodeIterator interface =============
/**
* The XStatement object is the NodeIterator root.
* @return itself.
*/
public Node getRoot()
{
if (DEBUG)
System.out.println("In XStatement.getRoot");
return this;
}
/**
* This attribute determines which node types are presented via the
* iterator. The available set of constants is defined in the
* <code>NodeFilter</code> interface.
*
* @return which node types are to be presented
*/
public int getWhatToShow()
{
if (DEBUG)
System.out.println("In XStatement.getWhatToShow");
// TODO: ??
return NodeFilter.SHOW_ALL & ~NodeFilter.SHOW_ENTITY_REFERENCE;
}
/**
* The filter used to screen nodes.
* @return null.
*/
public NodeFilter getFilter()
{
if (DEBUG)
System.out.println("In XStatement.getFilter");
return null;
}
/**
* The value of this flag determines whether the children of entity
* reference nodes are visible to the iterator. If false, they will be
* skipped over.
* <br> To produce a view of the document that has entity references
* expanded and does not expose the entity reference node itself, use the
* whatToShow flags to hide the entity reference node and set
* expandEntityReferences to true when creating the iterator. To produce
* a view of the document that has entity reference nodes but no entity
* expansion, use the whatToShow flags to show the entity reference node
* and set expandEntityReferences to false.
* @return true.
*/
public boolean getExpandEntityReferences()
{
if (DEBUG)
System.out.println("In XStatement.getExpandEntityReferences");
return true;
}
/**
* Return the #Document node (one role the XStatement plays) the first time called;
* return null thereafter.
* @return this or null.
*
* @throws DOMException
*/
public Node nextNode() throws DOMException
{
if (DEBUG)
System.out.println("In XStatement.nextNode");
if (!m_nextHasBeenCalled)
{
m_nextHasBeenCalled = true;
return this;
}
else
return null;
}
/**
* Throw an exception, since streaming nodes and iterators can not
* go backwards.
*
* @return null
*
* @throws DOMException
*/
public Node previousNode() throws DOMException
{
error(XSLTErrorResources.ER_FUNCTION_NOT_SUPPORTED); //"getParentNode not supported!");
return null;
}
/**
* Detaches the iterator from the set which it iterated over, releasing
* any computational resources and placing the iterator in the INVALID
* state. After<code>detach</code> has been invoked, calls to
* <code>nextNode</code> or<code>previousNode</code> will raise the
* exception INVALID_STATE_ERR.
*/
public void detach()
{
if (DEBUG)
System.out.println("XStatement.detach");
try
{
m_statement.close();
m_statement = null;
m_resultSet = null;
}
catch (SQLException sqle)
{
// diagnostics?
}
}
/**
* Return the String value of this object
*
*
* @return String value of the JDBC query string
*/
public String toString()
{
return "XStatement: " + m_queryString;
}
// ===== Document implementation ====
/**
* The parent of a document is null.
*
* @return null
*/
public Document getOwnerDocument()
{
return null;
}
/**
* Return node type: Node.DOCUMENT_NODE.
*
* @return Node.DOCUMENT_NODE.
*/
public short getNodeType()
{
return Node.DOCUMENT_NODE;
}
/**
* Return node name: "#Document".
*
* @return "#Document".
*/
public String getNodeName()
{
return "#Document";
}
/**
* Return the row-set node.
*
* @return the row-set node or null if not found.
*/
public Node getFirstChild()
{
if (DEBUG)
System.out.println("In XStatement.getFirstChild: "
+ this.getNodeTest());
try
{
if (getNodeTest() == null)
{
// We are not searching for any thing so return the row-set
return m_rowset;
}
if ((getNodeTest().getNamespace() == null)
&& (this.getNodeTest().getLocalName().equals(S_DOCELEMENTNAME)))
return m_rowset;
else
return null;
}
catch (NullPointerException e)
{
e.printStackTrace();
throw e;
}
}
/**
* getNextSibling - This always returns null.
*
* @return null
*/
public Node getNextSibling()
{
if (DEBUG)
System.out.println("In XStatement.getNextSibling");
return null;
}
/**
* The parent node of document is always null.
*
* @return null
*/
public Node getParentNode()
{
if (DEBUG)
System.out.println("In XStatement.getParentNode");
return null;
}
/**
* Tell if there are any children of the document,
* which is always true.
*
* @return true
*/
public boolean hasChildNodes()
{
if (DEBUG)
System.out.println("In XStatement.hasChildNodes");
return true;
}
// ===== ContextNodeList implementation =====
/**
* The current node is the RowSet
*
*
* @return The row-set
*/
public Node getCurrentNode()
{
return this.m_rowset;
}
/**
* Get Current Position
*
*
* @return 0
*/
public int getCurrentPos()
{
return 0; // Not totally sure...
}
/**
* Reset this object
*
*/
public void reset()
{
this.m_nextHasBeenCalled = false;
}
/**
*
* Determines if the data returned from the SQL Query should
* be used to build an in memory model of a DOM. If not, only
* the current row will be avaiable. Otherwise, build a memory
* model but that model will only be built up as the rows are
* traversed.
*
* @param b Flag indicating whether nodes should be cached
*
*/
public void setShouldCacheNodes(boolean b)
{
m_ShouldCacheNodes = b;
}
public boolean getShouldCacheNodes()
{
return m_ShouldCacheNodes;
}
/**
* Not implemented
*
*
* @param index
*/
public void runTo(int index)
{
// Throw exception if not streamable!
}
/**
* Not implemented
*
*
* @param i
*/
public void setCurrentPos(int i)
{
// Throw exception if not streamable!
}
/**
* Return size
*
*
* @return 1
*/
public int size()
{
return 1;
}
/**
* Return whether this iterator is fresh
*
*
* @return True if this has not been called
*/
public boolean isFresh()
{
return (!this.m_nextHasBeenCalled);
}
/**
* Overide cloneWithReset method
*
*
* @return A clone of this which has been reset
*
* @throws CloneNotSupportedException
*/
public NodeIterator cloneWithReset() throws CloneNotSupportedException
{
XStatement clone = (XStatement) super.clone();
clone.reset();
return clone;
}
/**
* Clone this object
*
*
* @return A clone of this object
*
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException
{
XStatement clone = (XStatement) super.clone();
return clone;
}
/** Index of Last node found by this iterator */
private int m_last = 0;
/**
* Get index of the last found node
*
*
* @return index of last found node
*/
public int getLast()
{
return m_last;
}
/**
* Set the index of the last found node
*
*
* @aram index of last found node
*/
public void setLast(int last)
{
m_last = last;
}
}