/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.panels.teststeps.support;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class NamedParameterStatement {
/**
* The statement this object is wrapping.
*/
private final PreparedStatement statement;
/**
* Maps parameter names to arrays of ints which are the parameter indices.
*/
private final Map indexMap;
/**
* Creates a NamedParameterStatement. Wraps a call to c.
* {@link Connection#prepareStatement(java.lang.String) prepareStatement}.
*
* @param connection the database connection
* @param query the parameterized query
* @throws SQLException if the statement could not be created
*/
public NamedParameterStatement(Connection connection, String query) throws SQLException {
indexMap = new HashMap();
String parsedQuery = parse(query, indexMap);
statement = connection.prepareStatement(parsedQuery);
}
/**
* Parses a query with named parameters. The parameter-index mappings are put
* into the map, and the parsed query is returned. DO NOT CALL FROM CLIENT
* CODE. This method is non-private so JUnit code can test it.
*
* @param query query to parse
* @param paramMap map to hold parameter-index mappings
* @return the parsed query
*/
private static final String parse(String query, Map paramMap) {
// I was originally using regular expressions, but they didn't work well
// for ignoring
// parameter-like strings inside quotes.
int length = query.length();
StringBuffer parsedQuery = new StringBuffer(length);
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
int index = 1;
for (int i = 0; i < length; i++) {
char c = query.charAt(i);
if (inSingleQuote) {
if (c == '\'') {
inSingleQuote = false;
}
} else if (inDoubleQuote) {
if (c == '"') {
inDoubleQuote = false;
}
} else {
if (c == '\'') {
inSingleQuote = true;
} else if (c == '"') {
inDoubleQuote = true;
} else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1))) {
int j = i + 2;
while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) {
j++;
}
String name = query.substring(i + 1, j);
c = '?'; // replace the parameter with a question mark
i += name.length(); // skip past the end if the parameter
List indexList = (List) paramMap.get(name);
if (indexList == null) {
indexList = new LinkedList();
paramMap.put(name, indexList);
}
indexList.add(new Integer(index));
index++;
}
}
parsedQuery.append(c);
}
// replace the lists of Integer objects with arrays of ints
for (Iterator itr = paramMap.entrySet().iterator(); itr.hasNext(); ) {
Map.Entry entry = (Map.Entry) itr.next();
List list = (List) entry.getValue();
int[] indexes = new int[list.size()];
int i = 0;
for (Iterator itr2 = list.iterator(); itr2.hasNext(); ) {
Integer x = (Integer) itr2.next();
indexes[i++] = x.intValue();
}
entry.setValue(indexes);
}
return parsedQuery.toString();
}
/**
* Returns the indexes for a parameter.
*
* @param name parameter name
* @return parameter indexes
* @throws IllegalArgumentException if the parameter does not exist
*/
private int[] getIndexes(String name) {
int[] indexes = (int[]) indexMap.get(name);
if (indexes == null) {
throw new IllegalArgumentException("Parameter '" + name + "' was not found in the SQL Query. ");
}
return indexes;
}
/**
* Sets a parameter.
*
* @param name parameter name
* @param value parameter value
* @throws SQLException if an error occurred
* @throws IllegalArgumentException if the parameter does not exist
* @see PreparedStatement#setObject(int, java.lang.Object)
*/
public void setObject(String name, Object value) throws SQLException {
int[] indexes = getIndexes(name);
for (int i = 0; i < indexes.length; i++) {
statement.setObject(indexes[i], value);
}
}
/**
* Sets a parameter.
*
* @param name parameter name
* @param value parameter value
* @throws SQLException if an error occurred
* @throws IllegalArgumentException if the parameter does not exist
* @see PreparedStatement#setString(int, java.lang.String)
*/
public void setString(String name, String value) throws SQLException {
int[] indexes = getIndexes(name);
for (int i = 0; i < indexes.length; i++) {
statement.setString(indexes[i], value);
}
}
/**
* Sets a parameter.
*
* @param name parameter name
* @param value parameter value
* @throws SQLException if an error occurred
* @throws IllegalArgumentException if the parameter does not exist
* @see PreparedStatement#setInt(int, int)
*/
public void setInt(String name, int value) throws SQLException {
int[] indexes = getIndexes(name);
for (int i = 0; i < indexes.length; i++) {
statement.setInt(indexes[i], value);
}
}
/**
* Sets a parameter.
*
* @param name parameter name
* @param value parameter value
* @throws SQLException if an error occurred
* @throws IllegalArgumentException if the parameter does not exist
* @see PreparedStatement#setInt(int, int)
*/
public void setLong(String name, long value) throws SQLException {
int[] indexes = getIndexes(name);
for (int i = 0; i < indexes.length; i++) {
statement.setLong(indexes[i], value);
}
}
/**
* Sets a parameter.
*
* @param name parameter name
* @param value parameter value
* @throws SQLException if an error occurred
* @throws IllegalArgumentException if the parameter does not exist
* @see PreparedStatement#setTimestamp(int, java.sql.Timestamp)
*/
public void setTimestamp(String name, Timestamp value) throws SQLException {
int[] indexes = getIndexes(name);
for (int i = 0; i < indexes.length; i++) {
statement.setTimestamp(indexes[i], value);
}
}
/**
* Returns the underlying statement.
*
* @return the statement
*/
public PreparedStatement getStatement() {
return statement;
}
/**
* Executes the statement.
*
* @return true if the first result is a {@link ResultSet}
* @throws SQLException if an error occurred
* @see PreparedStatement#execute()
*/
public boolean execute() throws SQLException {
return statement.execute();
}
/**
* Executes the statement, which must be a query.
*
* @return the query results
* @throws SQLException if an error occurred
* @see PreparedStatement#executeQuery()
*/
public ResultSet executeQuery() throws SQLException {
return statement.executeQuery();
}
/**
* Executes the statement, which must be an SQL INSERT, UPDATE or DELETE
* statement; or an SQL statement that returns nothing, such as a DDL
* statement.
*
* @return number of rows affected
* @throws SQLException if an error occurred
* @see PreparedStatement#executeUpdate()
*/
public int executeUpdate() throws SQLException {
return statement.executeUpdate();
}
/**
* Closes the statement.
*
* @throws SQLException if an error occurred
* @see Statement#close()
*/
public void close() throws SQLException {
statement.close();
}
/**
* Adds the current set of parameters as a batch entry.
*
* @throws SQLException if something went wrong
*/
public void addBatch() throws SQLException {
statement.addBatch();
}
/**
* Executes all of the batched statements.
* <p/>
* See {@link Statement#executeBatch()} for details.
*
* @return update counts for each statement
* @throws SQLException if something went wrong
*/
public int[] executeBatch() throws SQLException {
return statement.executeBatch();
}
}