/*
* codjo.net
*
* Common Apache License 2.0
*/
package net.codjo.control.common.util;
import net.codjo.reflect.util.ReflectHelper;
import java.beans.IntrospectionException;
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.Iterator;
import java.util.List;
/**
* Assistant pour le control d'entit�.
*
* @author $Author: gonnot $
* @version $Revision: 1.6 $
*/
public class EntityHelper {
private static final String ERROR_LOG = "ERROR_LOG";
private static final String ERROR_TYPE = "ERROR_TYPE";
private static final String QUARANTINE_ID = "QUARANTINE_ID";
private static final EntityResultState NO_ERROR_STATE =
new DefaultEntityResultState();
private PropertyConverter[] converters = {};
private String[] convertedProperty;
private ReflectHelper objectDef;
public EntityHelper() {
}
public EntityHelper(Class clazz) throws IntrospectionException {
objectDef = new ReflectHelper(clazz);
}
public void setBeanClassName(String className) {
if ("NONE".equals(className)) {
return;
}
try {
objectDef = new ReflectHelper(Class.forName(className));
}
catch (Exception ex) {
throw new IllegalArgumentException("Erreur d'initialisation " + className);
}
}
public void setConverters(PropertyConverter[] converters) {
this.converters = converters;
convertedProperty = null;
getConvertedProperty();
}
public Class getBeanClass() {
if (objectDef != null) {
return objectDef.getBeanClass();
}
return null;
}
public String getBeanClassName() {
return objectDef.getBeanClass().getName();
}
public PropertyConverter[] getConverters() {
return converters;
}
/**
* Insertion de l'objet dans la table de control.
*
* @param con
* @param bean
* @param tableName
*
* @throws SQLException
*/
public void insertIntoTable(Connection con, Object bean, String tableName)
throws SQLException {
String inserQuery = buildSQLInsert(tableName);
PreparedStatement stmt = con.prepareStatement(inserQuery);
try {
fillPreparedStatement(stmt, bean, true);
stmt.executeUpdate();
if (stmt.getWarnings() != null) {
throw stmt.getWarnings();
}
}
finally {
stmt.close();
}
}
public void updateTable(Connection con, Object vo, String tableName)
throws SQLException {
String updateQuery = buildSQLUpdate(tableName);
PreparedStatement stmt = con.prepareStatement(updateQuery);
try {
fillPreparedStatement(stmt, vo, false);
stmt.executeUpdate();
if (stmt.getWarnings() != null) {
throw stmt.getWarnings();
}
}
finally {
stmt.close();
}
}
/**
* Iterateur sur la table.
*
* @param con connection
* @param tableName table BD
*
* @return un EntityHelper
*
* @throws SQLException Erreur SQL
*/
public EntityIterator iterator(Connection con, String tableName)
throws SQLException {
Statement stmt =
con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
try {
ResultSet rs = stmt.executeQuery(buildSQLSelect(tableName));
return new EntityIteratorImpl(this, rs, stmt);
}
catch (SQLException ex) {
stmt.close();
throw ex;
}
}
/**
* Mise a jours du bean avec les infos control�s.
*
* @param con
* @param bean
* @param tableName
*
* @return Erreur sur l'entit� (ou null)
*
* @throws SQLException
* @throws IllegalStateException Erreur interne
*/
public EntityResultState updateObject(Connection con, Object bean, String tableName)
throws SQLException {
Statement stmt = con.createStatement();
try {
ResultSet rs =
stmt.executeQuery("select * from " + tableName + " where "
+ QUARANTINE_ID + "=1");
if (!rs.next()) {
throw new IllegalStateException("Enregistrement en control est absent");
}
int errorType = rs.getInt(ERROR_TYPE);
if (errorType > EntityResultState.NO_ERROR) {
return new DefaultEntityResultState(errorType, rs.getString(ERROR_LOG));
}
updateObject(bean, rs);
return NO_ERROR_STATE;
}
finally {
stmt.close();
}
}
public String buildSQLUpdate(String tableName) {
List<String> cols = new ArrayList<String>();
for (Iterator i = objectDef.shortPropertyNames(getConvertedProperty());
i.hasNext();) {
cols.add(toSqlName((String)i.next()));
}
for (int i = 0; i < getConvertedProperty().length; i++) {
cols.add(toSqlName(getConvertedProperty()[i]));
}
List<String> where = new ArrayList<String>();
where.add(QUARANTINE_ID);
return SQLUtil.buildUpdateQuery(tableName, cols, where);
}
protected String buildSQLInsert(String tableName) {
List<String> cols = new ArrayList<String>();
cols.add(QUARANTINE_ID);
for (Iterator i = objectDef.shortPropertyNames(getConvertedProperty());
i.hasNext();) {
cols.add(toSqlName((String)i.next()));
}
for (int i = 0; i < getConvertedProperty().length; i++) {
cols.add(toSqlName(getConvertedProperty()[i]));
}
return SQLUtil.buildInsertQuery(tableName, cols);
}
protected String buildSQLSelect(String tableName) {
// @ugly : Apres plusieurs essai, il s'avere que l'execution de la requete
// a partir de Weblogic necessite une 1ere colonne vide, sinon cela
// declenche une erreur sql JZ0SC.
List<String> cols = new ArrayList<String>();
cols.add("QUARANTINE_ID");
cols.add("ERROR_TYPE");
cols.add("ERROR_LOG");
for (Iterator i = objectDef.shortPropertyNames(); i.hasNext();) {
cols.add(toSqlName((String)i.next()));
}
return SQLUtil.buildSelectQuery(tableName, cols) + " where ERROR_TYPE <=0";
}
/**
* Transforme le nom d'une propri�t�s en son nom SQL. Exemple : <code>pimsCode</code> devient
* <code>PIMS_CODE</code>.
*
* @param propertyName propri�t�
*
* @return nom SQL
*/
protected String toSqlName(String propertyName) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < propertyName.length(); i++) {
if (Character.isUpperCase(propertyName.charAt(i)) && i > 0) {
buffer.append('_');
}
buffer.append(propertyName.charAt(i));
}
return buffer.toString().toUpperCase();
}
Object newObjectFrom(ResultSet rs)
throws SQLException, InstantiationException, IllegalAccessException {
Object obj = getBeanClass().newInstance();
updateObject(obj, rs);
return obj;
}
void updateResultSet(ResultSet rs, Object obj, EntityResultState state)
throws SQLException {
// Zapp la colonne inutile QUARANTINE_ID
if (state != null && state.getErrorType() != EntityResultState.NO_ERROR) {
rs.updateInt(2, state.getErrorType());
rs.updateString(3, state.getErrorLog());
return;
}
int idx = 3;
for (Iterator i = objectDef.shortPropertyNames(); i.hasNext();) {
String propertyName = (String)i.next();
Object value = objectDef.getPropertyValue(propertyName, obj);
if (value == null) {
rs.updateNull(++idx);
}
else {
rs.updateObject(++idx, value);
}
}
}
private void setStatement(final PreparedStatement stmt, final int idx,
final Object obj, final String propertyName)
throws SQLException {
int sqlType = SQLUtil.classToSqlType(objectDef.getPropertyClass(propertyName));
Object value = objectDef.getPropertyValue(propertyName, obj);
if (value == null) {
stmt.setNull(idx, sqlType);
}
else {
stmt.setObject(idx, value, sqlType);
}
}
private String[] getConvertedProperty() {
if (convertedProperty == null) {
convertedProperty = new String[converters.length];
for (int i = 0; i < converters.length; i++) {
convertedProperty[i] = converters[i].getPropertyName();
}
}
return convertedProperty;
}
private void fillPreparedStatement(final PreparedStatement stmt, final Object obj,
boolean pkFirst) throws SQLException {
int idx = 0;
// Positionnement de QUARANTINE_ID
if (pkFirst) {
stmt.setInt(++idx, 1);
}
for (Iterator i = objectDef.shortPropertyNames(getConvertedProperty());
i.hasNext();) {
String propertyName = (String)i.next();
setStatement(stmt, ++idx, obj, propertyName);
}
for (int i = 0; i < getConverters().length; i++) {
String propertyName = getConverters()[i].getPropertyPk();
setStatement(stmt, ++idx, obj, propertyName);
}
// Positionnement de QUARANTINE_ID
if (!pkFirst) {
stmt.setInt(++idx, 1);
}
}
private void updateObject(final Object bean, final ResultSet rs)
throws SQLException {
for (Iterator i = objectDef.shortPropertyNames(getConvertedProperty());
i.hasNext();) {
String shortPropertyName = (String)i.next();
objectDef.setPropertyValue(shortPropertyName, bean,
rs.getObject(toSqlName(shortPropertyName)));
}
for (int i = 0; i < getConverters().length; i++) {
PropertyConverter converter = getConverters()[i];
Object pk = rs.getObject(toSqlName(converter.getPropertyName()));
objectDef.setPropertyValue(converter.getPropertyName(), bean,
converter.load(null, pk));
}
}
}