/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.utils.sql; import net.codjo.gui.DetailButtonsPanel; import net.codjo.gui.NumberField; import net.codjo.gui.toolkit.date.DateField; import net.codjo.util.string.StringUtil; import net.codjo.utils.IntegerField; import net.codjo.utils.QueryHelper; import net.codjo.utils.SQLFieldList; import java.awt.Color; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import java.util.Map; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JInternalFrame; import javax.swing.JTextField; import javax.swing.text.JTextComponent; import org.apache.log4j.Logger; /** * Cette classe permet de construire des ecrans de detail. * * <p> Les sous-classes doivent definir deux JButton : <code>okButton</code> et <code>cancelButton</code> * </p> * * @author $Author: marcona $ * @version $Revision: 1.8 $ */ public class AbstractDetailWindow extends JInternalFrame implements DetailWindowInterface { // Log private static final Logger APP = Logger.getLogger(AbstractDetailWindow.class); /** * Ligne inseree dans la comboBox pour simuler une valeur <code>Null</code> . Voir <code>getValue</code> * . */ protected final String NULL_VALUE_COMBO = new String(" "); private final NumberFormat NUMBER_FORMAT_2DEC = new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.ENGLISH)); private final NumberFormat NUMBER_FORMAT_5DEC = new DecimalFormat("###,##0.00000", new DecimalFormatSymbols(Locale.ENGLISH)); private final NumberFormat NUMBER_FORMAT_6DEC = new DecimalFormat("###,##0.000000", new DecimalFormatSymbols(Locale.ENGLISH)); private final NumberFormat NUMBER_FORMAT_DEFAULT = NumberFormat.getNumberInstance(Locale.ENGLISH); private String actionType; /** * Constructor for the AbstractDetailWindow object */ public AbstractDetailWindow() { try { jbInit(); } catch (Exception ex) { ex.printStackTrace(); } } /** * Constructor for the AbstractDetailWindow object * * @param title Titre de la fenetre */ public AbstractDetailWindow(String title) { super(title); } /** * Retourne la Bouton "Appliquer". * * @return Le bouton ou null * * @throws IllegalArgumentException TODO */ public JButton getApplyButton() { try { Field field = getDeclaredField(getClass(), "applyButton"); return (JButton)field.get(this); } catch (NoSuchFieldException ex) { return null; } catch (IllegalAccessException ex) { throw new IllegalArgumentException( "Bouton Appliquer trouv� mais inaccessible"); } } /** * Retourne la Bouton "Annuler". * * @return Le bouton * * @throws IllegalArgumentException TODO */ public JButton getCancelButton() { try { Field field = getDeclaredField(getClass(), "cancelButton"); return (JButton)field.get(this); } catch (NoSuchFieldException ex) { try { Field field = getDeclaredField(getClass(), "detailButtonsPanel"); return ((DetailButtonsPanel)field.get(this)).getCancelButton(); } catch (NoSuchFieldException e1) { throw new IllegalArgumentException( "La fenetre ne definit pas de bouton Annuler"); } catch (IllegalAccessException e2) { throw new IllegalArgumentException( "Bouton Annuler trouv� dans le Panel mais inaccessible"); } } catch (IllegalAccessException ex) { throw new IllegalArgumentException("Bouton Annuler trouv� mais inaccessible"); } } public java.util.List getDeclaredFields(Class cl) throws SecurityException { if (cl == null) { return new ArrayList(); } java.util.List fields = getDeclaredFields(cl.getSuperclass()); fields.addAll(Arrays.asList(cl.getDeclaredFields())); return fields; } /** * Retourne la fenetre de detail. * * @return Une JInternalFrame */ public JInternalFrame getInternalFrame() { return this; } /** * Retourne les noms des attributs (composants graphiques) d�finis dans la fen�tre de d�tail. * * @return Liste des noms (String). * * @throws IllegalArgumentException TODO */ public java.util.List getListOfComponents() { java.util.List colsName = new ArrayList(); Field field = null; Field[] fieldArray = (Field[])getDeclaredFields(getClass()).toArray(new Field[]{}); try { for (int i = 0; i < fieldArray.length; i++) { field = fieldArray[i]; if (field.getModifiers() == Modifier.PUBLIC) { Object objectField = field.get(this); if (objectField instanceof JComponent) { colsName.add(field.getName()); } } } return colsName; } catch (IllegalAccessException ex) { throw new IllegalArgumentException("Composant inaccessible : " + field.getName()); } } /** * Retourne la Bouton "Suivant". * * @return Le bouton * * @throws IllegalArgumentException TODO */ public JButton getNextButton() { try { Field field = getDeclaredField(getClass(), "nextButton"); return (JButton)field.get(this); } catch (NoSuchFieldException ex) { try { Field field = getDeclaredField(getClass(), "detailButtonsPanel"); return ((DetailButtonsPanel)field.get(this)).getNextButton(); } catch (NoSuchFieldException e1) { return null; } catch (IllegalAccessException e2) { throw new IllegalArgumentException( "Bouton Suivant trouv� dans le Panel mais inaccessible"); } } catch (IllegalAccessException ex) { throw new IllegalArgumentException("Bouton Suivant trouv� mais inaccessible"); } } /** * Retourne la Bouton "Valider". * * @return Le bouton * * @throws IllegalArgumentException TODO */ public JButton getOkButton() { try { Field field = getDeclaredField(getClass(), "okButton"); return (JButton)field.get(this); } catch (NoSuchFieldException ex) { try { Field field = getDeclaredField(getClass(), "detailButtonsPanel"); return ((DetailButtonsPanel)field.get(this)).getOkButton(); } catch (NoSuchFieldException e1) { throw new IllegalArgumentException( "La fenetre ne definit pas de bouton OK"); } catch (IllegalAccessException e2) { throw new IllegalArgumentException( "Bouton OK trouv� dans le Panel mais inaccessible"); } } catch (IllegalAccessException ex) { throw new IllegalArgumentException("Bouton OK trouv� mais inaccessible"); } } /** * Retourne la Bouton "Pr�c�dent". * * @return Le bouton * * @throws IllegalArgumentException TODO */ public JButton getPreviousButton() { try { Field field = getDeclaredField(getClass(), "previousButton"); return (JButton)field.get(this); } catch (NoSuchFieldException ex) { try { Field field = getDeclaredField(getClass(), "detailButtonsPanel"); return ((DetailButtonsPanel)field.get(this)).getPreviousButton(); } catch (NoSuchFieldException e1) { return null; } catch (IllegalAccessException e2) { throw new IllegalArgumentException( "Bouton Pr�c�dent trouv� dans le Panel mais inaccessible"); } } catch (IllegalAccessException ex) { throw new IllegalArgumentException( "Bouton Pr�c�dent trouv� mais inaccessible"); } } /** * Positionne le type d'action qui a caus� l'ouverture de l'�cran de d�tail (ajout ou modification). * * @param actionType Le type d'action (add ou modify) */ public void setActionType(String actionType) { this.actionType = actionType; } /** * Retourne le type d'action qui a caus� l'ouverture de l'�cran de d�tail (ajout ou modification). * * @return Le type d'action (add ou modify) */ public String getActionType() { return actionType; } /** * Rempli les JTextComponent et les JCheckBox des �crans page avec les valeurs des champs correspondant. * * @param columns Liste des colonnes * @param rs ResultSet des valeurs � renseigner * * @throws SQLException - * @throws IllegalArgumentException TODO */ public void fillComponent(SQLFieldList columns, ResultSet rs) throws SQLException { if (rs.next() == false) { throw new IllegalArgumentException("Manque une ligne"); } for (Iterator iter = columns.fieldNames(); iter.hasNext();) { String columnName = (String)iter.next(); setValue(columnName, rs.getObject(columnName)); } for (Iterator iter = columns.fieldNames(); iter.hasNext();) { String columnName = (String)iter.next(); initModificationListener(columnName); } } /** * Rempli un JTextComponent ou un JCheckBox des �crans page avec une valeur par d�faut. * * @param defaultValues Description of Parameter */ public void fillDefaultValues(HashMap defaultValues) { for (Iterator iter = defaultValues.keySet().iterator(); iter.hasNext();) { String fieldName = (String)iter.next(); Object value = defaultValues.get(fieldName); setValue(fieldName, value); } } /** * Remplit le QueryHelper avec les valeurs des colonnes avec les champs de la fenetre detail. * * @param columns Liste de colonnes a remplir dans le query helper. * @param qh Le QueryHelper a remplir. */ public void fillQueryHelper(SQLFieldList columns, QueryHelper qh) { Iterator iter = columns.fieldNames(); while (iter.hasNext()) { String columnName = (String)iter.next(); Object value = getValue(columnName, columns.getFieldType(columnName)); qh.setInsertValue(columnName, value); } } /** * Par defaut, rien n'est fait. * * @param pk TODO * @param con TODO */ public void saveLinks(Map pk, Connection con) throws Exception { } /** * Positionne la valeur du combobox. Cette methode gere aussi la valeur null * * @param cb Le ComboBox * @param columnValue La valeur */ private void setComboBoxValue(JComboBox cb, Object columnValue) { if (columnValue == null) { cb.setSelectedIndex(-1); } else { boolean itemFound = false; int count = cb.getItemCount(); for (int i = 0; i < count; i++) { if (columnValue.equals(cb.getItemAt(i))) { itemFound = true; cb.setSelectedIndex(i); break; } } if (!itemFound) { cb.addItem(columnValue); cb.setSelectedItem(columnValue); } } } /** * Renseigne la valeur du champ de la colonne. * * @param columnName Le nom physique de la colonne * @param columnValue La valeur du champ � renseigner * * @throws IllegalArgumentException TODO */ private void setValue(String columnName, Object columnValue) { try { Field field = getDeclaredField(getClass(), columnName); Object objectField = field.get(this); if (objectField == null) { field.set(this, columnValue); } else if (objectField instanceof NumberField || objectField instanceof IntegerField) { if (columnValue == null) { ((JTextField)objectField).setText(""); } else { ((JTextField)objectField).setText(columnValue.toString()); } } else if (objectField instanceof JTextComponent) { if (columnValue == null) { ((JTextComponent)objectField).setText(""); } else if (columnValue instanceof java.util.Date) { DateFormat df = new SimpleDateFormat("dd/MM/yyyy"); ((JTextComponent)objectField).setText(df.format(columnValue)); } else if (columnValue instanceof java.lang.Number) { if (columnValue instanceof java.lang.Integer) { ((JTextComponent)objectField).setText(columnValue.toString()); } else { BigDecimal number = new BigDecimal(columnValue.toString()); int NbDec = number.scale(); switch (NbDec) { case 2: ((JTextComponent)objectField).setText(NUMBER_FORMAT_2DEC.format( (columnValue))); break; case 5: ((JTextComponent)objectField).setText(NUMBER_FORMAT_5DEC.format( (columnValue))); break; case 6: ((JTextComponent)objectField).setText(NUMBER_FORMAT_6DEC.format( (columnValue))); break; default: ((JTextComponent)objectField).setText(NUMBER_FORMAT_DEFAULT.format( (columnValue))); } } } else { ((JTextComponent)objectField).setText(columnValue.toString()); } ((JTextComponent)objectField).setCaretPosition(0); } else if (objectField instanceof JComboBox) { setComboBoxValue((JComboBox)objectField, columnValue); } else if (objectField instanceof JCheckBox) { ((JCheckBox)objectField).setSelected(((Boolean)columnValue).booleanValue()); } else if (objectField instanceof DateField) { if (columnValue instanceof java.util.Date) { ((DateField)objectField).setDate((java.util.Date)(columnValue)); } } else if (!(objectField instanceof JComponent)) { field.set(this, columnValue); } else { throw new IllegalArgumentException("Type Composant inconnu : " + columnName); } } catch (NoSuchFieldException ex) { APP.debug("Le champs " + columnName + " est introuvable (" + getTitle() + ")", ex); return; } catch (IllegalAccessException ex) { throw new IllegalArgumentException("Composant inaccessible : " + columnName); } } /** * Recherche le field <code>name</code> dans la classe courante et aussi dans les sous-classes. * * @param cl la classe de recherche * @param name le nom du champs * * @return Le Field * * @throws NoSuchFieldException Le field n'existe pas * @throws SecurityException Le field est private. */ private Field getDeclaredField(Class cl, String name) throws NoSuchFieldException, SecurityException { if (cl == null) { throw new NoSuchFieldException(name); } try { return cl.getDeclaredField(name); } catch (NoSuchFieldException ex) { return getDeclaredField(cl.getSuperclass(), name); } catch (SecurityException ex) { return getDeclaredField(cl.getSuperclass(), name); } } /** * Retourne la valeur du champ de la colonne. * * @param columnName Le nom physique de la colonne * @param sqlType Le type sql du champs * * @return la valeur. * * @throws IllegalArgumentException TODO */ protected Object getValue(String columnName, int sqlType) { try { Field field = getDeclaredField(getClass(), columnName); Object o = field.get(this); if (o instanceof JTextComponent) { try { ((JTextComponent)o).setForeground(Color.black); return translateValue(((JTextComponent)o).getText(), columnName, sqlType); } catch (IllegalArgumentException ex) { ((JTextComponent)o).setForeground(Color.red); throw ex; } } else if (o instanceof JComboBox) { Object result = ((JComboBox)o).getSelectedItem(); if (NULL_VALUE_COMBO == result) { return null; } else { return result; } } else if (o instanceof JCheckBox) { return new Boolean(((JCheckBox)o).isSelected()); } else if (o instanceof DateField) { return ((DateField)o).getDate(); } else { throw new IllegalArgumentException("Composant inconnu : " + columnName); } } catch (NoSuchFieldException ex) { return null; } catch (IllegalAccessException ex) { throw new IllegalArgumentException("Composant inaccessible : " + columnName); } } /** * Ajoute un ModificationListener � AbstractDetailWindow. * * @param columnName Le ModificationListener � ajouter */ private void initModificationListener(String columnName) { try { Field field = getDeclaredField(getClass(), columnName); Object fieldValue = field.get(this); if ((fieldValue instanceof JComponent) == false) { return; } JComponent component = (JComponent)fieldValue; ModificationListener oldML = (ModificationListener)component.getClientProperty("modificationListener"); if (oldML != null) { oldML.clear(); return; } if (component instanceof JTextComponent) { ModificationListener ml = new ModificationListener(component); ((JTextComponent)component).getDocument().addDocumentListener(ml); component.putClientProperty("modificationListener", ml); } else if (component instanceof JComboBox) { ModificationListener ml = new ModificationListener(component); ((JComboBox)component).addActionListener(ml); component.putClientProperty("modificationListener", ml); } else if (component instanceof JCheckBox) { ModificationListener ml = new ModificationListener(component); ((JCheckBox)component).addActionListener(ml); component.putClientProperty("modificationListener", ml); } } catch (Exception nsfe) { nsfe.printStackTrace(); } } /** * Init IHM. * * @throws Exception Description of Exception */ private void jbInit() throws Exception { } /** * Converti le texte dans le format bd. * * @param textValue Valeur sous forme String * @param columnName Le nom physique de la colonne * @param sqlType type sql du format * * @return Valeur convertie. * * @throws IllegalArgumentException TODO */ protected Object translateValue(String textValue, String columnName, int sqlType) { if ("".equals(textValue)) { return null; } switch (sqlType) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return textValue; case Types.INTEGER: case Types.SMALLINT: case Types.TINYINT: return Integer.decode(textValue); case Types.FLOAT: return new Float(StringUtil.removeAllCharOccurrence(textValue, ',')); case Types.DOUBLE: return new Double(StringUtil.removeAllCharOccurrence(textValue, ',')); case Types.NUMERIC: return new BigDecimal(StringUtil.removeAllCharOccurrence(textValue, ',')); case Types.BIT: return new Boolean(textValue); case Types.DATE: case Types.TIMESTAMP: DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.FRANCE); df.setLenient(false); try { return df.parse(textValue); } catch (ParseException ex) { throw new IllegalArgumentException("Mauvais format de date" + " pour le champ " + columnName + " (format DD/MM/AAAA)"); } default: throw new IllegalArgumentException("Type SQL non supporte : " + columnName); } } }