/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.utils.sql; //Lib import net.codjo.model.Table; import net.codjo.persistent.Reference; import java.sql.Connection; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.table.AbstractTableModel; import org.apache.log4j.Logger; /** * Model d'affichage generique du contenu d'une table BD. * * @author $Author: blazart $ * @version $Revision: 1.5 $ */ public class GenericTableModel extends AbstractTableModel { //Le buffer de chargement de ligne /** Description of the Field */ protected static final int BUFFER_SIZE = 1000; private static final Logger APP = Logger.getLogger(GenericTableModel.class); //Encore des donn�es apr�s cette page private boolean babyOneMoreTime; //Tableau des classes des colonnes private Class[] columnClass; //Liste des noms physiques des colonnes private List columnDBNameList; //Liste des libell�s des colonnes private List columnLabelList; //Liste des donn�es private List dataList; //Les droits d'edition par cellule private List editableColumnList; //La connection forc�e private Connection forcedConnection; //Derniere clause where private String fromAndWhereClause = ""; //Liste des noms des champs cl�s primaires private List keyDBNameList; //Liste des cl�s private List keyValueList; //Num�ro du premier enregistrement affich� private int numberOfFirstRow; //Num�ro du premier enregistrement affich� private int numberOfLastRow; //Nombre de lignes de la derniere requete private int numberOfRows; // Clause de order by private String orderByClause = null; //La table est en read-only private boolean readOnly; //La requete de r�cup�ration des donn�es private String request; //Table destination private Reference tableRef; //Mode de selection Distinct private boolean distinctMode = false; /** * Constructor complet pour l'objet GenericTableModel * * @param con Description of Parameter * @param tableDestRef Description of Parameter * @param columnList La liste des DB noms de colonnes * @param editableColumnList Description of Parameter * @param readOnly Le composant est en read only * @param whereClause La clause where initiale (peut �tre vide) * @param orderByClause Clause order by (eg "order by LABEL") * * @exception SQLException SQL * @throws IllegalArgumentException TODO */ public GenericTableModel(Connection con, Reference tableDestRef, List columnList, List editableColumnList, boolean readOnly, String whereClause, String orderByClause) throws SQLException { if (tableDestRef == null || columnList == null) { throw new IllegalArgumentException("Param�tres non valides"); } forcedConnection = con; tableRef = tableDestRef; columnDBNameList = columnList; this.readOnly = readOnly; setOrderByClause(orderByClause); columnLabelList = new ArrayList(columnDBNameList.size()); this.editableColumnList = editableColumnList; columnClass = new Class[columnDBNameList.size()]; initKeysDBNames(); initRequest(whereClause); initFieldLabel(); initColumnClassName(); loadData(0); } /** * Retourne la classe d'une colonne du model * * @param columnIndex Le num�ro de colonne * * @return La classe de la colonne */ public Class getColumnClass(int columnIndex) { return columnClass[columnIndex]; } /** * Retourne le nombre de colonnes du model * * @return The ColumnCount value */ public int getColumnCount() { return columnDBNameList.size(); } /** * Retourne la liste des noms physiques des champs du model * * @return Une liste de string */ public List getColumnDBNameList() { return columnDBNameList; } /** * Retourne le nom d'une colonne du model * * @param columnIndex Le num�ro de colonne * * @return Le libell� de la colonne */ public String getColumnName(int columnIndex) { return (String)columnLabelList.get(columnIndex); } /** * Retourne le num�ro de la colonne / dbName * * @param columnDBName Le nom physique de la colonne * * @return La position de la colonne * * @throws IllegalArgumentException TODO */ public int getColumnNumber(String columnDBName) { for (int i = 0; i < this.columnDBNameList.size(); i++) { if (columnDBName.equals(columnDBNameList.get(i))) { return i; } } throw new IllegalArgumentException("Colonne " + columnDBName + " non trouv�e dans le model"); } /** * Retourne une connection utilisable. La methode releaseConnection() doit etre * imperativement appele apres utilisation * * @return La connection * * @exception SQLException Description of Exception */ public Connection getConnection() throws SQLException { if (forcedConnection != null) { return forcedConnection; } else { return Dependency.getConnectionManager().getConnection(); } } /** * Retourne l'attribut orderByClause de GenericTableModel * * @return La valeur de orderByClause */ public String getOrderByClause() { return orderByClause; } /** * Retourne le nombre de lignes dans le model * * @return Nombre de lignes */ public int getRowCount() { return dataList.size(); } /** * Renvoie un element du model � afficher * * @param row Le num�ro de ligne � afficher * @param column Le num�ro de colonne � afficher * * @return La valeur correspondant au num�ros de ligne et de colonne * * @throws IllegalArgumentException TODO */ public Object getValueAt(int row, int column) { List aRow = null; if ((column >= 0) && (column < getColumnCount()) && (row >= 0) && (row < dataList.size())) { aRow = (List)dataList.get(row); return aRow.get(column); } else if (column == -1) { return new Integer(row); } else { throw new IllegalArgumentException("Cellule inconnue L=" + row + " C=" + column); } } /** * La cellule est'elle editable ? * * @param row Num�ro de la ligne (on ne g�re pas) * @param col Num�ro de la colonne * * @return Editable ? */ public boolean isCellEditable(int row, int col) { if (readOnly) { return false; } else { return ((Boolean)editableColumnList.get(col)).booleanValue(); } } /** * DOCUMENT ME! * * @param con * * @exception SQLException */ public void releaseConnection(Connection con) throws SQLException { if (con == null) { return; } if (forcedConnection == con) { return; } Dependency.getConnectionManager().releaseConnection(con); } /** * DOCUMENT ME! * * @param con * @param stmt * * @exception SQLException */ public void releaseConnection(Connection con, Statement stmt) throws SQLException { try { if (stmt != null) { stmt.close(); } } catch(Exception e){ //To hell !!!! } finally { releaseConnection(con); } } /** * Change la clause <code>order by</code> de rafraichissement de la table. Aucun * rechargement est effectu�. * * @param orderByClause La clause avec le mot clef "order by" (ex: "order by LABEL * desc, ID") * * @exception SQLException */ public void setOrderByClause(String orderByClause) throws SQLException { this.orderByClause = orderByClause; } /** * Stocke la nouvelle valeur dans les donn�es * * @param value La nouvelle valeur * @param row Le num�ro de la ligne * @param col Le num�ro de la colonne * * @throws Error TODO */ public void setValueAt(Object value, int row, int col) { if (readOnly) { throw new Error( "Impossible de modifier une cellule si le composant est en mode lecture seule"); } else { List aRow = (List)dataList.get(row); aRow.set(col, value); fireTableCellUpdated(row, col); } } /** * La selection des lignes s'effectuera en mode disctinct ou pas * * @param distinctMode Mode select distinct ? */ void setDistinctModeOn(boolean distinctMode) { this.distinctMode = distinctMode; } /** * D�finir une nouvelle connexion � utiliser * * @param con La nouvelle connexion */ void setNewConnection(Connection con) { forcedConnection = con; } /** * Ajoute une ligne dans le model * * @throws RuntimeException TODO * @throws IllegalArgumentException TODO */ void addNewLine() { if (readOnly) { throw new RuntimeException( "Impossible d'ajouter une ligne si le composant est en mode lecture seule"); } //On cr�� une ligne pour les donn�es List newBlankLineList = new ArrayList(); for (int i = 0; i < columnDBNameList.size(); i++) { if (columnClass[i] == Boolean.class) { newBlankLineList.add(new Boolean(false)); } else if (columnClass[i] == Date.class) { newBlankLineList.add(null); } else if (columnClass[i] == Integer.class) { newBlankLineList.add(null); } else if (columnClass[i] == String.class) { newBlankLineList.add(null); } else if (columnClass[i] == Number.class) { newBlankLineList.add(null); } else { throw new IllegalArgumentException(); } } dataList.add(0, newBlankLineList); //On cr�� une ligne vide pour les cl�s List newkeyValueLineList = new ArrayList(); keyValueList.add(0, newkeyValueLineList); fireTableDataChanged(); } /** * Supprime une ligne dans le model * * @param index Num�ro de la ligne � virer * * @throws RuntimeException TODO */ void deleteLine(int index) { if (readOnly) { throw new RuntimeException( "Impossible de supprimer une ligne si le composant est en mode lecture seule"); } dataList.remove(index); keyValueList.remove(index); fireTableDataChanged(); } /** * R�cup�re la liste des valeurs des cl�s d'une ligne si c'est une nouvelle ligne, * les cl�s ne sont pas d�finies et la m�thode renvoie donc null * * @param index Le num�ro de ligne * * @return Une hashMap contenant les valeurs des cl�s ou null */ Map getALineOfKey(int index) { if (index > keyValueList.size() - 1) { return null; } List keyList = (List)keyValueList.get(index); if (keyList.size() != 0) { Map hm = new HashMap(); for (int i = 0; i < keyList.size(); i++) { hm.put(keyDBNameList.get(i), keyList.get(i)); } return hm; } else { return null; } } /** * Retourne le num�ro de la premiere ligne de la page courante * * @return Le num�ro */ int getNumberOfFirstRow() { return numberOfFirstRow; } /** * Retourne le num�ro de la derni�re ligne de la page courante * * @return Le num�ro */ int getNumberOfLastRow() { return numberOfLastRow; } /** * Retourne le nombre total d'enregistrements dans la table (correspondants � la * dern�re clause where appliqu�e) * * @return Le nombre de lignes total */ int getNumberOfRows() { return numberOfRows; } /** * Reste-t'il des donn�es apr�s la page courante * * @return Oui ou Non ! */ boolean hasMoreData() { return babyOneMoreTime; } /** * Reconstruit la requete et lance le rechargement des donn�es * * @param newFromAndWhereClause Description of Parameter * @param page Description of Parameter * * @exception SQLException Description of Exception */ void reloadData(String newFromAndWhereClause, int page) throws SQLException { fromAndWhereClause = newFromAndWhereClause; initBodyOfRequest(); request = request + " " + newFromAndWhereClause; if (getOrderByClause() != null) { request += " " + getOrderByClause(); } loadData(page); fireTableDataChanged(); } /** * Retourne la table * * @return La table */ private Table getTable() { return (Table)tableRef.getLoadedObject(); } /** * Construit le corps de la requete (la partie invariante) jusqu'au 'from' (exclus) * * @exception SQLException Description of Exception */ private void initBodyOfRequest() throws SQLException { if (columnDBNameList.size() == 0) { throw new SQLException( "Aucune colonne � afficher n'est param�tr�e pour le composant GenericTable"); } //Les champs � afficher request = "select "; if (distinctMode) { request += "distinct "; } request += getTable().getDBTableName() + "." + columnDBNameList.get(0); for (int i = 1; i < columnDBNameList.size(); i++) { request = request + ", " + getTable().getDBTableName() + "." + columnDBNameList.get(i); } //Les champs qui sont des cl�s primaires for (int i = 0; i < keyDBNameList.size(); i++) { request = request + ", " + getTable().getDBTableName() + "." + keyDBNameList.get(i); } request = request + " "; } /** * Charge les classes de colonnes affich�es dans le model; * * @throws RuntimeException TODO */ private void initColumnClassName() { for (int i = 0; i < columnDBNameList.size(); i++) { int sqlType = getTable().getColumnSqlType((String)columnDBNameList.get(i)); switch (sqlType) { case Types.BIT: columnClass[i] = Boolean.class; break; case Types.DATE: columnClass[i] = Date.class; break; case Types.INTEGER: columnClass[i] = Integer.class; break; case Types.VARCHAR: columnClass[i] = String.class; break; case Types.CHAR: columnClass[i] = String.class; break; case Types.NUMERIC: columnClass[i] = Number.class; break; case Types.TIMESTAMP: columnClass[i] = Date.class; break; case Types.TINYINT: case Types.SMALLINT: columnClass[i] = Integer.class; break; case Types.LONGVARCHAR: columnClass[i] = String.class; break; case Types.FLOAT: columnClass[i] = Float.class; break; case Types.DOUBLE: columnClass[i] = Double.class; break; default: throw new RuntimeException("Type de colonne non support� : " + sqlType + "=>" + columnDBNameList.get(i)); } } } /** * Charge les libell�s de champs affich�s * * @exception SQLException Description of Exception */ private void initFieldLabel() throws SQLException { String query; Statement stmt = null; Connection con = getConnection(); try { stmt = con.createStatement(); for (int i = 0; i < columnDBNameList.size(); i++) { query = "select FIELD_LABEL from PM_FIELD_LABEL " + "where DB_TABLE_NAME='" + this.getTable().getDBTableName() + "' and DB_FIELD_NAME='" + columnDBNameList.get(i) + "'"; ResultSet rs = stmt.executeQuery(query); if (rs.next()) { columnLabelList.add(i, rs.getString("FIELD_LABEL")); } else { columnLabelList.add(i, columnDBNameList.get(i)); } } } finally { releaseConnection(con, stmt); } } /** * Initialise la liste des noms des champs �tant des cl�s primaires. * * @exception SQLException : un pb de base ? */ private void initKeysDBNames() throws SQLException { keyDBNameList = new ArrayList(getTable().getPkNames()); } /** * Cr�ation de la requ�te de recup�ration des donn�es * * @param whereClause Le texte de la clause where * * @exception SQLException Pas de param�trage de colonne � afficher trouv� en BD */ private void initRequest(String whereClause) throws SQLException { initBodyOfRequest(); fromAndWhereClause = " from " + this.getTable().getDBTableName() + " " + whereClause; request = request + fromAndWhereClause; if (getOrderByClause() != null) { request += " " + getOrderByClause(); } } /** * Charge les donn�es (liste des cl�s et des donn�es � afficher) * * @param page Description of Parameter * * @exception SQLException Description of Exception */ private void loadData(int page) throws SQLException { dataList = new ArrayList(); keyValueList = new ArrayList(); numberOfFirstRow = (page * BUFFER_SIZE) + 1; numberOfLastRow = (page + 1) * BUFFER_SIZE; BufferLoader sw = new BufferLoader(page); // sw.start(); // TEMP sw.loadBuffer(); sw.fillData(); // END TEMP } /** * Description of the Class * * @author VIRASIS * @version $Revision: 1.5 $ */ private class BufferLoader { List dataListTH = new ArrayList(); List keyValueListTH = new ArrayList(); int numberOfRowsTH; int page; /** * DOCUMENT ME! * * @param page */ BufferLoader(int page) { this.page = page; } /** * Description of the Method */ public void fillData() { numberOfRows = numberOfRowsTH; dataList = dataListTH; keyValueList = keyValueListTH; fireTableDataChanged(); } /** * Description of the Method * * @exception SQLException Description of Exception */ public void loadBuffer() throws SQLException { APP.debug("Begin load Data with " + request); int line = 0; Statement stmt = null; Connection con = getConnection(); try { stmt = con.createStatement(); //Recherche du nombre de lignes ResultSet rs = stmt.executeQuery("select count(*) " + fromAndWhereClause); if (rs.next()) { numberOfRowsTH = rs.getInt(1); } rs.close(); stmt.setFetchSize(BUFFER_SIZE); rs = stmt.executeQuery(request); while (rs.next() && (line < numberOfLastRow)) { line++; if ((line >= numberOfFirstRow) && (line <= numberOfLastRow)) { //On stocke les donn�es d'une ligne List dataLineList = new ArrayList(); for (int i = 0; i < columnDBNameList.size(); i++) { Object obj = rs.getObject(i + 1); dataLineList.add(obj); } dataListTH.add(dataLineList); //On stocke les cl�s d'une ligne List keyValueLineList = new ArrayList(); for (int i = 0; i < keyDBNameList.size(); i++) { keyValueLineList.add(rs.getObject(columnDBNameList.size() + i + 1)); } keyValueListTH.add(keyValueLineList); } } if (rs.isAfterLast()) { numberOfLastRow = numberOfRowsTH; babyOneMoreTime = false; } else if (line == 0) { numberOfFirstRow = 0; numberOfLastRow = 0; babyOneMoreTime = false; } else { babyOneMoreTime = true; } } finally { releaseConnection(con, stmt); APP.debug("End load Data with " + request); } } } public List getKeyValueList() { return keyValueList; } }