package de.unioninvestment.eai.portal.portlet.crud.scripting.model; import static java.util.Arrays.asList; import de.unioninvestment.eai.portal.portlet.crud.scripting.domain.container.database.QueryStatementGenerator; import groovy.lang.GString; import groovy.sql.Sql; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.codehaus.groovy.runtime.GStringImpl; import org.springframework.util.Assert; import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; import de.unioninvestment.eai.portal.portlet.crud.domain.exception.TechnicalCrudPortletException; import de.unioninvestment.eai.portal.portlet.crud.domain.model.DataContainer; import de.unioninvestment.eai.portal.portlet.crud.domain.model.DatabaseQueryContainer; /** * Groovy Script Wrapper class for the {@link DatabaseQueryContainer}. Provides * special operations that only work with the query backend. * * @author carsten.mjartan */ public class ScriptDatabaseQueryContainer extends ScriptDatabaseContainer { private QueryStatementGenerator insertGenerator, updateGenerator, deleteGenerator; /** * This class acts as dynamic proxy for a {@link PreparedStatement} and * collects calls of * {@link StatementHelper#setParameterValuesToStatement(PreparedStatement)}. * * It provides a values list for the Query GString. * * @author carsten.mjartan */ private static final class ParameterInvocationHandler implements InvocationHandler { LinkedList<Object> values = new LinkedList<Object>(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("setObject")) { values.add(args[1]); } else if (method.getName().equals("setNull")) { values.add(null); } else if (method.getName().equals("setBigDecimal")) { values.add(args[1]); } else if (method.getName().equals("setDate")) { values.add(args[1]); } else if (method.getName().equals("setString")) { values.add(args[1]); } else if (method.getName().equals("setTime")) { values.add(args[1]); } else if (method.getName().equals("setTimestamp")) { values.add(args[1]); } else { throw new UnsupportedOperationException( "Cannot handle call to " + method + " with args " + asList(args)); } return null; } public Object[] getValues() { return values.toArray(); } } private DatabaseQueryContainer databaseQueryContainer; ScriptDatabaseQueryContainer(DataContainer container) { super(container); this.databaseQueryContainer = (DatabaseQueryContainer) container; } void setInsertGenerator(QueryStatementGenerator insertGenerator) { this.insertGenerator = insertGenerator; } void setUpdateGenerator(QueryStatementGenerator updateGenerator) { this.updateGenerator = updateGenerator; } void setDeleteGenerator(QueryStatementGenerator deleteGenerator) { this.deleteGenerator = deleteGenerator; } /** * Returns the database query that respects the filter criteria and possibly * a sort order of the current view. * * @param options * currently only 'preserveOrder':boolean is supported * @return a GString that can be used in {@link Sql} GString operations */ public GString getCurrentQuery(Map<String, Object> options) { boolean preserveOrder = options != null && Boolean.TRUE.equals(options.get("preserveOrder")); StatementHelper helper = databaseQueryContainer .getCurrentQuery(preserveOrder); GString result = gstringify(helper); return result; } private GString gstringify(StatementHelper helper) { String queryString = helper.getQueryString(); String[] strings = extractGStrings(queryString); int expectedValueLength = queryString.endsWith("?") ? strings.length : strings.length - 1; ParameterInvocationHandler handler = new ParameterInvocationHandler(); PreparedStatement stmt = (PreparedStatement) Proxy.newProxyInstance( this.getClass().getClassLoader(), new Class<?>[] { PreparedStatement.class }, handler); try { helper.setParameterValuesToStatement(stmt); } catch (SQLException e) { throw new TechnicalCrudPortletException( "Error catching parameter values", e); } Object[] values = handler.getValues(); Assert.state(values.length == expectedValueLength, "Inconsistent string length, conversion failed"); return new GStringImpl(values, strings); } private String[] extractGStrings(String queryString) { // String[] strings = queryString.split("\\?"); List<String> result = new ArrayList<String>(); int start = 0; boolean inQuotes = false; int length = queryString.length(); for (int current = 0; current < length; current++) { if (queryString.charAt(current) == '\'') { inQuotes = !inQuotes; // toggle state } else if (!inQuotes) { if (queryString.charAt(current) == '?') { result.add(queryString.substring(start, current)); start = current + 1; } } } if (start < length) { result.add(queryString.substring(start)); } return result.toArray(new String[result.size()]); } public GString generateUpdateStatement(ScriptRow row) { return updateGenerator.generateStatement(row); } public GString generateInsertStatement(ScriptRow row) { return insertGenerator.generateStatement(row); } public GString generateDeleteStatement(ScriptRow row) { return deleteGenerator.generateStatement(row); } }