/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.rdfbean.virtuoso;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import com.mysema.query.QueryException;
import com.mysema.rdfbean.model.NODE;
import com.mysema.rdfbean.model.SPARQLQuery;
/**
* @author tiwe
*
*/
public abstract class AbstractQueryImpl implements SPARQLQuery {
protected static final Pattern VARIABLE = Pattern.compile("\\?[a-zA-Z_]\\w*");
private final Connection connection;
private final int prefetch;
protected PreparedStatement stmt;
protected ResultSet rs;
protected final String query;
protected final Map<String, NODE> bindings = new HashMap<String, NODE>();
public AbstractQueryImpl(Connection connection, int prefetch, String query) {
this.connection = connection;
this.prefetch = prefetch;
this.query = query;
}
@Override
public final void setBinding(String variable, NODE node) {
bindings.put(variable, node);
}
@Override
public final void setMaxQueryTime(int secs) {
// do nothing
}
protected void close() {
close(stmt, rs);
}
public static void close(@Nullable Statement stmt, @Nullable ResultSet rs) {
try {
try {
if (rs != null) {
rs.close();
}
} finally {
if (stmt != null) {
stmt.close();
}
}
} catch (SQLException e) {
throw new QueryException(e);
}
}
protected ResultSet executeQuery(String query, boolean createAliases) throws SQLException {
if (bindings.isEmpty()) {
stmt = connection.prepareStatement(query); // NOSONAR
} else {
List<NODE> nodes = new ArrayList<NODE>(bindings.size());
String normalized = normalize(query, bindings, nodes, createAliases);
stmt = connection.prepareStatement(normalized); // NOSONAR
int offset = 1;
for (NODE node : nodes) {
if (node.isResource()) {
VirtuosoRepositoryConnection.bindResource(stmt, offset++, node.asResource());
} else {
VirtuosoRepositoryConnection.bindValue(stmt, offset, node.asLiteral());
offset += 3;
}
}
}
stmt.setFetchSize(prefetch);
rs = stmt.executeQuery();
return rs;
}
static String normalize(String query, Map<String, NODE> bindings, List<NODE> nodes, boolean createAliases) {
String queryLower = query.toLowerCase(Locale.ENGLISH);
Matcher matcher = VARIABLE.matcher(query);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String variable = matcher.group().substring(1);
String replacement = matcher.group();
boolean unquoted = isUnquoted(queryLower, matcher);
if (bindings.containsKey(variable)) {
NODE node = bindings.get(variable);
nodes.add(node);
if (node.isResource()) {
replacement = unquoted ? "iri(??)" : "`iri(??)`";
} else {
replacement = unquoted ? "bif:__rdf_long_from_batch_params(??,??,??)" : "`bif:__rdf_long_from_batch_params(??,??,??)`";
}
if (createAliases
&& !queryLower.substring(0, matcher.start()).contains("where")
&& !queryLower.substring(0, matcher.start()).contains("from")) {
replacement = replacement + " as ?" + variable;
}
}
matcher.appendReplacement(buffer, replacement);
}
matcher.appendTail(buffer);
return buffer.toString();
}
private static boolean isUnquoted(String query, Matcher matcher) {
int i;
for (i = matcher.start() - 1; i >= 0; i--) {
char c = query.charAt(i);
if (c == '}' || c == '{') {
return false;
}
if (c == '(') {
break;
}
}
if (i > 0) {
for (i = matcher.end(); i < query.length(); i++) {
char c = query.charAt(i);
if (c == '}' || c == '{') {
return false;
}
if (c == ')' || c == '(') {
return true;
}
}
return false;
} else {
return true;
}
}
}