/*******************************************************************************
* Copyright (c) 2014 BestSolution.at and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
*******************************************************************************/
package at.bestsolution.persistence.java;
import java.io.IOException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.text.StrLookup;
import org.apache.commons.lang.text.StrSubstitutor;
import at.bestsolution.persistence.Function;
import at.bestsolution.persistence.java.JavaSession.ChangeDescription;
import at.bestsolution.persistence.java.query.JDBCType;
import at.bestsolution.persistence.java.query.TypedValue;
public class Util {
public static class ProcessedSQL {
public final String sql;
public final List<String> dynamicParameterNames;
public final Map<String, List<TypedValue>> listValueMaps;
public ProcessedSQL(String sql, List<String> dynamicParameterNames, Map<String, List<TypedValue>> listValueMaps) {
this.sql = sql;
this.dynamicParameterNames = dynamicParameterNames;
this.listValueMaps = listValueMaps == null ? new HashMap<String, List<TypedValue>>() : listValueMaps;
}
}
public static class SimpleQueryBuilder {
private final String tableName;
private final List<Column> columns = new ArrayList<Util.Column>();
public SimpleQueryBuilder(String tableName) {
this.tableName = tableName;
}
public final void addColumn(String columnName, String dynamicParameter) {
columns.add(new Column(columnName, dynamicParameter));
}
public final ProcessedSQL buildUpdate(String pkColumn,
String pkColumnParameter, String lockColumn) {
List<String> dynamicValues = new ArrayList<String>();
StringBuilder b = new StringBuilder();
if( lockColumn != null ) {
b.append( '"' + lockColumn.toUpperCase() + '"' + " = " + '"' + lockColumn.toUpperCase() + '"' + " + 1");
}
for (Column c : columns) {
if (b.length() != 0) {
b.append("\n,");
}
b.append('"' + c.columnName + '"' + " = ?");
dynamicValues.add(c.dynamicParameter);
}
dynamicValues.add(pkColumnParameter);
return new ProcessedSQL("UPDATE " + '"' + tableName + '"' + " SET " + b
+ " WHERE " + '"' + pkColumn + '"' + " = ?", dynamicValues,null);
}
public final ProcessedSQL buildInsert(String pkColumn,
String valueExpression, String lockColumn) {
StringBuilder col = new StringBuilder();
StringBuilder val = new StringBuilder();
List<String> dynamicValues = new ArrayList<String>();
if (pkColumn != null) {
col.append('"'+pkColumn+'"');
val.append(valueExpression);
}
if( lockColumn != null ) {
if (col.length() != 0) {
col.append("\n,");
val.append("\n,");
}
col.append('"'+lockColumn.toUpperCase()+'"');
val.append("0");
}
for (Column c : columns) {
if (col.length() != 0) {
col.append("\n,");
val.append("\n,");
}
col.append('"'+c.columnName+'"');
val.append("?");
dynamicValues.add(c.dynamicParameter);
}
return new ProcessedSQL("INSERT INTO "+'"' + tableName + '"' +"(" + col
+ ") VALUES (" + val + ")", dynamicValues,null);
}
}
public static class Column {
final String columnName;
final String dynamicParameter;
public Column(final String columnName, final String dynamicParameter) {
this.columnName = columnName;
this.dynamicParameter = dynamicParameter;
}
}
public static final String loadFile(Class<?> clazz, String name) {
InputStream inputStream = clazz.getResourceAsStream(name);
if (inputStream == null) {
return null;
}
byte[] buf = new byte[1024];
int l;
StringBuilder b = new StringBuilder();
try {
while ((l = inputStream.read(buf)) != -1) {
b.append(new String(buf, 0, l));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return b.toString();
}
public static final ProcessedSQL processSQL(String sql, final Function<String, List<?>> listLookup) {
final List<String> dynamicParameterNames = new ArrayList<String>();
final Map<String,List<TypedValue>> typedValuesMap = new HashMap<String, List<TypedValue>>();
String s = new StrSubstitutor(new StrLookup() {
@Override
public String lookup(String key) {
List<?> data = listLookup.execute(key);
if( data == null ) {
dynamicParameterNames.add(key);
return "?";
} else {
List<TypedValue> list = null;
StringBuilder rv = new StringBuilder();
for( Object o : data ) {
if( rv.length() > 0 ) {
rv.append(", ");
}
if( o == null ) {
rv.append("NULL");
} else if( o instanceof Long || o instanceof Integer ) {
rv.append(o);
} else {
if( list == null ) {
list = new ArrayList<TypedValue>();
typedValuesMap.put(key,list);
}
list.add(new TypedValue(o, JDBCType.fromJavaType(o.getClass())));
rv.append("?");
}
}
return rv.toString();
}
}
}, "#{", "}", '#').replace(sql);
return new ProcessedSQL(s, dynamicParameterNames,null);
}
public static final ProcessedSQL processSQL(String sql) {
final List<String> dynamicParameterNames = new ArrayList<String>();
String s = new StrSubstitutor(new StrLookup() {
@Override
public String lookup(String key) {
dynamicParameterNames.add(key);
return "?";
}
}, "#{", "}", '#').replace(sql);
return new ProcessedSQL(s, dynamicParameterNames,null);
}
public static final SimpleQueryBuilder createQueryBuilder(String tableName) {
return new SimpleQueryBuilder(tableName);
}
public static void setValue(PreparedStatement pstmt, int parameterIndex, TypedValue value) throws SQLException {
if (value.value == null) {
int sqlType;
switch (value.type) {
case INT:
sqlType = Types.INTEGER;
break;
case DOUBLE:
sqlType = Types.DECIMAL;
break;
case FLOAT:
sqlType = Types.FLOAT;
break;
case BOOLEAN:
sqlType = Types.BOOLEAN;
break;
case LONG:
sqlType = Types.BIGINT;
break;
case STRING:
sqlType = Types.VARCHAR;
break;
case BLOB:
sqlType = Types.BLOB;
break;
case CLOB:
sqlType = Types.CLOB;
break;
case TIMESTAMP:
sqlType = Types.TIMESTAMP;
break;
default:
sqlType = Types.OTHER;
break;
}
pstmt.setNull(parameterIndex, sqlType);
} else {
switch (value.type) {
case INT:
pstmt.setInt(parameterIndex, ((Number)value.value).intValue());
break;
case DOUBLE:
pstmt.setDouble(parameterIndex, ((Number)value.value).doubleValue());
break;
case FLOAT:
pstmt.setDouble(parameterIndex, ((Number)value.value).doubleValue());
break;
case BOOLEAN:
pstmt.setBoolean(parameterIndex, Boolean.TRUE.equals(value.value) );
break;
case LONG:
pstmt.setLong(parameterIndex, ((Number)value.value).longValue());
break;
case STRING:
pstmt.setString(parameterIndex, (String)value.value);
break;
case TIMESTAMP:
if( value.value instanceof Timestamp ) {
pstmt.setTimestamp(parameterIndex, (Timestamp)value.value);
} else {
pstmt.setTimestamp(parameterIndex, new Timestamp(((Date)value.value).getTime()));
}
break;
case UNKNOWN:
pstmt.setObject(parameterIndex, value.value);
break;
default:
throw new IllegalStateException("Unknown type");
}
}
}
public static boolean isModified(JavaSession session, Object object, String attributeName) {
for( ChangeDescription d : session.getChangeDescription(object) ) {
if( d.getFeature().getName().equals(attributeName) ) {
return true;
}
}
return false;
}
public static Map<String, Object> mapResultSet(ResultSet set) throws SQLException {
Map<String,Object> map = new HashMap<String, Object>();
ResultSetMetaData m = set.getMetaData();
int columnCount = m.getColumnCount();
for( int i = 0; i < columnCount; i++ ) {
map.put(m.getColumnName(i),set.getObject(i));
}
return map;
}
public static void trimToSize(List<?> listToTrim, int size) {
listToTrim.subList(size, listToTrim.size()).clear();
}
public static <O> void syncLists(List<O> targetList, List<O> newList) {
Iterator<O> it = targetList.iterator();
List<O> removeItems = new ArrayList<O>();
// remove items not found in new list
// do not remove immediately because because then to many notifications
// are regenerated
while( it.hasNext() ) {
O next = it.next();
if( ! newList.contains(next) ) {
removeItems.add(next);
}
}
targetList.removeAll(removeItems);
// remove all items from the new list already contained
it = newList.iterator();
while( it.hasNext() ) {
if( targetList.contains(it.next()) ) {
it.remove();
}
}
targetList.addAll(newList);
}
public static String[] splitOfSegment(String propertyName) {
String reference = propertyName.substring(0,propertyName.indexOf('.'));
propertyName = propertyName.substring(propertyName.indexOf('.')+1,propertyName.length());
return new String[] { reference, propertyName };
}
}