/*
* Copyright (C) Lennart Martens
*
* Contact: lennart.martens AT UGent.be (' AT ' to be replaced with '@')
*/
/*
* Created by IntelliJ IDEA.
* User: Lennart
* Date: 29-dec-02
* Time: 14:24:22
*/
package com.compomics.util.db.components;
import org.apache.log4j.Logger;
import com.compomics.util.db.DBMetaData;
/*
* CVS information:
*
* $Revision: 1.5 $
* $Date: 2009/07/23 10:59:18 $
*/
/**
* This class generates the code to make a DBAccessor an implementation of the Retrieveable
* interface.
*
* @author Lennart Martens
*/
public class RetrievableCode {
// Class specific log4j logger for RetrievableCode instances.
Logger logger = Logger.getLogger(RetrievableCode.class);
/**
* This String will hold the generated code.
*/
private String iCode = null;
/**
* This constructor allows the generation of the code for the implementation of
* the Retrievable interface for a DBAccessor class, based on the specified metadata.
*
* @param aMeta DBMetaData with the metadata to generate the code for.
*/
public RetrievableCode(DBMetaData aMeta) {
// First get the primary key columns.
String[] pkCols = aMeta.getPrimaryKeyColumns();
// Next get all column names and types.
String[] names = aMeta.getColumnNames();
String[] types = aMeta.getConvertedColumnTypes();
// The tablename.
String table = aMeta.getTableName();
// The 'select' query.
StringBuffer query = new StringBuffer("\"SELECT * FROM " + table + " WHERE ");
for(int i = 0; i < pkCols.length; i++) {
String lCol = pkCols[i];
// First one does not have leading 'AND'.
if(i == 0) {
query.append(lCol + " = ?");
} else {
query.append(" AND " + lCol + " = ?");
}
}
query.append("\"");
// Now generate the code.
StringBuffer lsb = new StringBuffer(
"\t/**\n\t * This method allows the caller to read data for this\n\t * object from a persistent store based on the specified keys.\n");
lsb.append("\t *\n\t * @param aConn Connection to the persitent store.\n\t */\n");
lsb.append("\tpublic void retrieve(Connection aConn, HashMap aKeys) throws SQLException {\n");
lsb.append("\t\t// First check to see whether all PK fields are present.\n");
for(int i=0;i<pkCols.length;i++) {
lsb.append("\t\tif(!aKeys.containsKey(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ")) {\n");
lsb.append("\t\t\tthrow new IllegalArgumentException(\"Primary key field '" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase())
+ "' is missing in HashMap!\");\n");
lsb.append("\t\t} else {\n");
String lCol = pkCols[i];
String name = "i" + lCol.substring(0,1).toUpperCase() + lCol.substring(1).toLowerCase();
lsb.append("\t\t\t" + name);
// Check the type. If it is Object or array, a cast suffices. If it is a primitive, we'll have to accommodate.
String type = aMeta.getConvertedColumnType(i);
if(Character.isLowerCase(type.charAt(0)) && !type.endsWith("[]")) {
// Primitive. Let's accommodate.
if(type.equals("int")) {
lsb.append(" = ((Integer)aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ")).intValue();\n");
} else if(type.equals("long")) {
lsb.append(" = ((Long)aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ")).longValue();\n");
} else if(type.equals("double")) {
lsb.append(" = ((Double)aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ")).doubleValue();\n");
} else if(type.equals("char")) {
lsb.append(" = ((Character)aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ")).charValue();\n");
} else if(type.equals("boolean")) {
lsb.append(" = ((Boolean)aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ")).booleanValue();\n");
} else {
// Do an object cast as well. Better then nothing...
lsb.append(" = (" + aMeta.getConvertedColumnType(i) + ")aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ");\n");
}
} else {
// Object type, cast directly.
lsb.append(" = (" + aMeta.getConvertedColumnType(i) + ")aKeys.get(" + (Character.isDigit(pkCols[i].charAt(0))?"i"+pkCols[i].toUpperCase():pkCols[i].toUpperCase()) + ");\n");
}
lsb.append("\t\t}\n");
}
lsb.append("\t\t// In getting here, we probably have all we need to continue. So let's...\n");
lsb.append("\t\tPreparedStatement lStat = aConn.prepareStatement(" + query.toString() + ");\n");
for(int i = 0; i < pkCols.length; i++) {
String lCol = pkCols[i];
String name = "i" + lCol.substring(0,1).toUpperCase() + lCol.substring(1).toLowerCase();
String type = aMeta.getConvertedColumnType(lCol);
// See if we have an Object type, or something else.
if(Character.isUpperCase(type.charAt(0)) || type.indexOf(".") >= 0) {
lsb.append("\t\tlStat.setObject(" + (i+1) + ", " + name + ");\n");
} else {
if(type.equals("byte[]")) {
// A Byte[] is set using an inputstream.
lsb.append("\t\tByteArrayInputStream bais" + i + " = new ByteArrayInputStream(" + name + ");\n");
lsb.append("\t\tlStat.setBinaryStream(" + (i+1) + ", bais" + i + ", " + name + ".length);\n");
} else if(type.equals("int")) {
lsb.append("\t\tlStat.setInt(" + (i+1) + ", " + name + ");\n");
} else if(type.equals("long")) {
lsb.append("\t\tlStat.setLong(" + (i+1) + ", " + name + ");\n");
} else if(type.equals("double")) {
lsb.append("\t\tlStat.setDouble(" + (i+1) + ", " + name + ");\n");
} else if(type.equals("char")) {
lsb.append("\t\tlStat.setObject(" + (i+1) + ", new Character(" + name + "));\n");
} else if(type.equals("boolean")) {
lsb.append("\t\tlStat.setBoolean(" + (i+1) + ", " + name + ");\n");
}
}
}
lsb.append("\t\tResultSet lRS = lStat.executeQuery();\n");
lsb.append("\t\tint hits = 0;\n");
lsb.append("\t\twhile(lRS.next()) {\n");
lsb.append("\t\t\thits++;\n");
for(int i = 0; i < names.length; i++) {
String name = names[i];
String varName = "i" + name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
String type = types[i];
// Object types are easy.
// Others need some specific treatment.
if(Character.isUpperCase(type.charAt(0)) || type.indexOf(".") >= 0) {
lsb.append("\t\t\t" + varName + " = (" + type + ")lRS.getObject(\"" + name + "\");\n");
} else {
if(type.equals("byte[]")) {
lsb.append("\t\t\tInputStream is" + i + " = lRS.getBinaryStream(\"" + name + "\");\n");
lsb.append("\t\t\tVector bytes" + i + " = new Vector();\n");
lsb.append("\t\t\tint reading = -1;\n");
lsb.append("\t\t\ttry {\n");
lsb.append("\t\t\t\twhile((reading = is" + i + ".read()) != -1) {\n");
lsb.append("\t\t\t\t\tbytes" + i + ".add(new Byte((byte)reading));\n");
lsb.append("\t\t\t\t}\n");
lsb.append("\t\t\t\tis" + i + ".close();\n");
lsb.append("\t\t\t} catch(IOException ioe) {\n");
lsb.append("\t\t\t\tbytes" + i + " = new Vector();\n");
lsb.append("\t\t\t}\n");
lsb.append("\t\t\treading = bytes" + i + ".size();\n");
lsb.append("\t\t\t" + varName + " = new byte[reading];\n");
lsb.append("\t\t\tfor(int i=0;i<reading;i++) {\n");
lsb.append("\t\t\t\t" + varName + "[i] = ((Byte)bytes" + i + ".get(i)).byteValue();\n");
lsb.append("\t\t\t}\n");
} else if(type.equals("int")) {
lsb.append("\t\t\t" + varName + " = lRS.getInt(\"" + name + "\");\n");
} else if(type.equals("long")) {
lsb.append("\t\t\t" + varName + " = lRS.getLong(\"" + name + "\");\n");
} else if(type.equals("double")) {
lsb.append("\t\t\t" + varName + " = lRS.getDouble(\"" + name + "\");\n");
} else if(type.equals("char")) {
lsb.append("\t\t\tString temp" + varName + " = lRS.getString(\"" + name + "\");\n");
lsb.append("\t\t\tif(temp" + varName + " != null) {\n");
lsb.append("\t\t\t\t" + varName + " = temp" + varName + ".charAt(0);\n");
lsb.append("\t\t\t} else {\n");
lsb.append("\t\t\t\t" + varName + " = ' ';\n");
lsb.append("\t\t\t}\n");
} else if(type.equals("boolean")) {
lsb.append("\t\t\t" + varName + " = lRS.getBoolean(\"" + name + "\");\n");
}
}
}
lsb.append("\t\t}\n");
lsb.append("\t\tlRS.close();\n");
lsb.append("\t\tlStat.close();\n");
lsb.append("\t\tif(hits>1) {\n");
lsb.append("\t\t\tthrow new SQLException(\"More than one hit found for the specified primary keys in the '"
+ table + "' table! Object is initialized to last row returned.\");\n");
lsb.append("\t\t} else if(hits == 0) {\n");
lsb.append("\t\t\tthrow new SQLException(\"No hits found for the specified primary keys in the '"
+ table + "' table! Object is not initialized correctly!\");\n");
lsb.append("\t\t}\n");
lsb.append("\t}\n");
// Add a simple static 'basic select' getter.
lsb.append("\t/**\n\t * This method allows the caller to obtain a basic select for this table.\n");
lsb.append("\t *\n");
lsb.append("\t * @return String with the basic select statement for this table.\n");
lsb.append("\t */\n");
lsb.append("\tpublic static String getBasicSelect(){\n");
lsb.append("\t\treturn \"select * from " + table + "\";\n");
lsb.append("\t}\n\n");
// Add another static convenience method that retrieves all the entries in the DB for this table.
String tableAsClassnameStart = table.substring(0, 1).toUpperCase() + table.substring(1).toLowerCase();
lsb.append("\t/**\n\t * This method allows the caller to obtain all rows for this\n\t * table from a persistent store.\n");
lsb.append("\t *\n\t * @param aConn Connection to the persitent store.\n");
lsb.append("\t * @return ArrayList<" + tableAsClassnameStart + "TableAccessor> with all entries for this table.\n\t */\n");
lsb.append("\tpublic static ArrayList<" + tableAsClassnameStart + "TableAccessor> retrieveAllEntries(Connection aConn) throws SQLException {\n");
lsb.append("\t\tArrayList<" + tableAsClassnameStart + "TableAccessor> entities = new ArrayList<" + tableAsClassnameStart + "TableAccessor>();\n");
lsb.append("\t\tStatement stat = aConn.createStatement();\n");
lsb.append("\t\tResultSet rs = stat.executeQuery(getBasicSelect());\n");
lsb.append("\t\twhile(rs.next()) {\n");
lsb.append("\t\t\tentities.add(new " + tableAsClassnameStart + "TableAccessor(rs));\n");
lsb.append("\t\t}\n");
lsb.append("\t\trs.close();\n");
lsb.append("\t\tstat.close();\n");
lsb.append("\t\treturn entities;\n");
lsb.append("\t}\n\n");
this.iCode = lsb.toString();
}
/**
* This method will report on the generated code.
*
* @return String with the generated code.
*/
public String toString() {
return "\n" + iCode + "\n";
}
}