/*
* Copyright 2011 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.data.util.sqlcontainer.query.generator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.sqlcontainer.ColumnProperty;
import com.vaadin.data.util.sqlcontainer.RowItem;
import com.vaadin.data.util.sqlcontainer.SQLUtil;
import com.vaadin.data.util.sqlcontainer.TemporaryRowId;
import com.vaadin.data.util.sqlcontainer.query.OrderBy;
import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator;
/**
* Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL.
*
* @author Jonatan Kronqvist / IT Mill Ltd
*/
@SuppressWarnings("serial")
public class DefaultSQLGenerator implements SQLGenerator {
public DefaultSQLGenerator() {
}
/**
* Construct a DefaultSQLGenerator with the specified identifiers for start
* and end of quoted strings. The identifiers may be different depending on
* the database engine and it's settings.
*
* @param quoteStart
* the identifier (character) denoting the start of a quoted
* string
* @param quoteEnd
* the identifier (character) denoting the end of a quoted string
*/
public DefaultSQLGenerator(String quoteStart, String quoteEnd) {
QueryBuilder.setStringDecorator(new StringDecorator(quoteStart,
quoteEnd));
}
/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateSelectQuery(java.lang.String, java.util.List, java.util.List,
* int, int, java.lang.String)
*/
public StatementHelper generateSelectQuery(String tableName,
List<Filter> filters, List<OrderBy> orderBys, int offset,
int pagelength, String toSelect) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
toSelect = toSelect == null ? "*" : toSelect;
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("SELECT " + toSelect + " FROM ").append(
SQLUtil.escapeSQL(tableName));
if (filters != null) {
query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
}
if (orderBys != null) {
for (OrderBy o : orderBys) {
generateOrderBy(query, o, orderBys.indexOf(o) == 0);
}
}
if (pagelength != 0) {
generateLimits(query, offset, pagelength);
}
sh.setQueryString(query.toString());
return sh;
}
/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateUpdateQuery(java.lang.String,
* com.vaadin.addon.sqlcontainer.RowItem)
*/
public StatementHelper generateUpdateQuery(String tableName, RowItem item) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
if (item == null) {
throw new IllegalArgumentException("Updated item must be given.");
}
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("UPDATE ").append(tableName).append(" SET");
/* Generate column<->value and rowidentifiers map */
Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
Map<String, Object> rowIdentifiers = generateRowIdentifiers(item);
/* Generate columns and values to update */
boolean first = true;
for (String column : columnToValueMap.keySet()) {
if (first) {
query.append(" " + QueryBuilder.quote(column) + " = ?");
} else {
query.append(", " + QueryBuilder.quote(column) + " = ?");
}
sh.addParameterValue(columnToValueMap.get(column), item
.getItemProperty(column).getType());
first = false;
}
/* Generate identifiers for the row to be updated */
first = true;
for (String column : rowIdentifiers.keySet()) {
if (first) {
query.append(" WHERE " + QueryBuilder.quote(column) + " = ?");
} else {
query.append(" AND " + QueryBuilder.quote(column) + " = ?");
}
sh.addParameterValue(rowIdentifiers.get(column), item
.getItemProperty(column).getType());
first = false;
}
sh.setQueryString(query.toString());
return sh;
}
/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateInsertQuery(java.lang.String,
* com.vaadin.addon.sqlcontainer.RowItem)
*/
public StatementHelper generateInsertQuery(String tableName, RowItem item) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
if (item == null) {
throw new IllegalArgumentException("New item must be given.");
}
if (!(item.getId() instanceof TemporaryRowId)) {
throw new IllegalArgumentException(
"Cannot generate an insert query for item already in database.");
}
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("INSERT INTO ").append(tableName).append(" (");
/* Generate column<->value map */
Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
/* Generate column names for insert query */
boolean first = true;
for (String column : columnToValueMap.keySet()) {
if (!first) {
query.append(", ");
}
query.append(QueryBuilder.quote(column));
first = false;
}
/* Generate values for insert query */
query.append(") VALUES (");
first = true;
for (String column : columnToValueMap.keySet()) {
if (!first) {
query.append(", ");
}
query.append("?");
sh.addParameterValue(columnToValueMap.get(column), item
.getItemProperty(column).getType());
first = false;
}
query.append(")");
sh.setQueryString(query.toString());
return sh;
}
/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateDeleteQuery(java.lang.String,
* com.vaadin.addon.sqlcontainer.RowItem)
*/
public StatementHelper generateDeleteQuery(String tableName,
List<String> primaryKeyColumns, String versionColumn, RowItem item) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
if (item == null) {
throw new IllegalArgumentException(
"Item to be deleted must be given.");
}
if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
throw new IllegalArgumentException(
"Valid keyColumnNames must be provided.");
}
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("DELETE FROM ").append(tableName).append(" WHERE ");
int count = 1;
for (String keyColName : primaryKeyColumns) {
if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
&& keyColName.equalsIgnoreCase("rownum")) {
count++;
continue;
}
if (count > 1) {
query.append(" AND ");
}
if (item.getItemProperty(keyColName).getValue() != null) {
query.append(QueryBuilder.quote(keyColName) + " = ?");
sh.addParameterValue(item.getItemProperty(keyColName)
.getValue(), item.getItemProperty(keyColName).getType());
}
count++;
}
if (versionColumn != null) {
query.append(String.format(" AND %s = ?",
QueryBuilder.quote(versionColumn)));
sh.addParameterValue(
item.getItemProperty(versionColumn).getValue(), item
.getItemProperty(versionColumn).getType());
}
sh.setQueryString(query.toString());
return sh;
}
/**
* Generates sorting rules as an ORDER BY -clause
*
* @param sb
* StringBuffer to which the clause is appended.
* @param o
* OrderBy object to be added into the sb.
* @param firstOrderBy
* If true, this is the first OrderBy.
* @return
*/
protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o,
boolean firstOrderBy) {
if (firstOrderBy) {
sb.append(" ORDER BY ");
} else {
sb.append(", ");
}
sb.append(QueryBuilder.quote(o.getColumn()));
if (o.isAscending()) {
sb.append(" ASC");
} else {
sb.append(" DESC");
}
return sb;
}
/**
* Generates the LIMIT and OFFSET clause.
*
* @param sb
* StringBuffer to which the clause is appended.
* @param offset
* Value for offset.
* @param pagelength
* Value for pagelength.
* @return StringBuffer with LIMIT and OFFSET clause added.
*/
protected StringBuffer generateLimits(StringBuffer sb, int offset,
int pagelength) {
sb.append(" LIMIT ").append(pagelength).append(" OFFSET ")
.append(offset);
return sb;
}
protected Map<String, Object> generateColumnToValueMap(RowItem item) {
Map<String, Object> columnToValueMap = new HashMap<String, Object>();
for (Object id : item.getItemPropertyIds()) {
ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
/* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
&& cp.getPropertyId().equalsIgnoreCase("rownum")) {
continue;
}
Object value = cp.getValue() == null ? null : cp.getValue();
/* Only include properties whose read-only status can be altered */
if (cp.isReadOnlyChangeAllowed() && !cp.isVersionColumn()) {
columnToValueMap.put(cp.getPropertyId(), value);
}
}
return columnToValueMap;
}
protected Map<String, Object> generateRowIdentifiers(RowItem item) {
Map<String, Object> rowIdentifiers = new HashMap<String, Object>();
for (Object id : item.getItemPropertyIds()) {
ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
/* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
&& cp.getPropertyId().equalsIgnoreCase("rownum")) {
continue;
}
Object value = cp.getValue() == null ? null : cp.getValue();
if (!cp.isReadOnlyChangeAllowed() || cp.isVersionColumn()) {
rowIdentifiers.put(cp.getPropertyId(), value);
}
}
return rowIdentifiers;
}
}