/**
* SQL-DK
* Copyright © 2013 František Kučera (frantovo.cz)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package info.globalcode.sql.dk;
import static info.globalcode.sql.dk.Functions.notNull;
import static info.globalcode.sql.dk.Functions.escapeRegEx;
import static info.globalcode.sql.dk.Functions.findByName;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author Ing. František Kučera (frantovo.cz)
*/
public class SQLCommandNamed extends SQLCommand {
private static final String PROBLEM_MARK = "<OMG>";
private String namePrefix;
private String nameSuffix;
private List<NamedParameter> parameters;
private List<NamedParameter> parametersUsed = new ArrayList<>();
private StringBuilder updatedQuery;
private Pattern pattern;
public SQLCommandNamed(String query, List<NamedParameter> parameters, String namePrefix, String nameSuffix) {
super(query);
this.updatedQuery = new StringBuilder(query.length());
this.parameters = parameters;
this.namePrefix = namePrefix;
this.nameSuffix = nameSuffix;
}
@Override
public PreparedStatement prepareStatement(Connection c) throws SQLException {
buildPattern();
placeParametersAndUpdateQuery();
return c.prepareStatement(updatedQuery.toString());
}
@Override
public void parametrize(PreparedStatement ps) throws SQLException {
int i = 1;
for (Parameter p : notNull(parametersUsed)) {
ps.setObject(i++, p.getValue(), p.getType());
}
}
/**
* Builds a regexp pattern that matches all parameter names (with prefix/suffix) and which has
* one group: parameter name (without prefix/suffix)
*/
private void buildPattern() {
StringBuilder patternString = new StringBuilder();
patternString.append(escapeRegEx(namePrefix));
patternString.append("(");
for (int i = 0; i < parameters.size(); i++) {
patternString.append(escapeRegEx(parameters.get(i).getName()));
if (i < parameters.size()) {
patternString.append("|");
}
}
patternString.append(")");
patternString.append(escapeRegEx(nameSuffix));
pattern = Pattern.compile(patternString.toString());
}
private void placeParametersAndUpdateQuery() throws SQLException {
final String originalQuery = getQuery();
Matcher m = pattern.matcher(originalQuery);
int lastPosition = 0;
while (m.find(lastPosition)) {
String name = m.group(1);
updatedQuery.append(originalQuery.substring(lastPosition, m.start()));
if (name.isEmpty()) {
updatedQuery.append(PROBLEM_MARK);
updatedQuery.append(originalQuery.substring(m.end(), originalQuery.length()));
throw new SQLException("Named parameter (near " + PROBLEM_MARK + ") is not defined: " + updatedQuery);
}
updatedQuery.append("?");
parametersUsed.add(findByName(parameters, name));
lastPosition = m.end();
}
updatedQuery.append(originalQuery.substring(lastPosition, originalQuery.length()));
for (NamedParameter definedParameter : parameters) {
if (findByName(parametersUsed, definedParameter.getName()) == null) {
throw new SQLException("Parameter " + definedParameter.getName() + " is defined but not used in the query.");
}
}
}
@Override
public List<NamedParameter> getParameters() {
return parameters;
}
}