/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.persistent.sql; // Persistance import net.codjo.persistent.PersistenceException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.SQLException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.StringTokenizer; import org.apache.log4j.Logger; /** * Classe implemantant le mecanisme de traduction utilise par <code>SimpleHome * </code>lors du chargement d'un objet. * * <p> * Cette classe contient la liste de tout les traducteurs pour un home specifique. Un * traducteur est initialise par la clause <code>translator.xx=yy[.zz]</code> , ou xx * est un nom de propriete, yy est l'objet responsable de la traduction (en general un * autre Home), et zz est le nom de la methode faisant la traduction (optionnel). * </p> * * <p> * Ce <code>SimpleHomeTranslator</code> utilisera (pour zz) la methode prenant comme * parametre un <code>int</code> , et si elle n'existe pas, la premiere methode public * nomme "zz". * </p> * * <p> * Le mot clef <code>this</code> peut etre utilise pour yy. Dans ce cas, la methode de * traduction se trouve sur le home courant. * </p> * * <p> * Si <code>zz</code> n'est pas precise, le traducteur <code>yy</code> doit contenir une * methode : <code>getxx(...)</code> retournant un objet. Cette methode ne sera pas * appele pour les valeur <code>null</code> . * </p> * * @author $Author: blazart $ * @version $Revision: 1.4 $ */ class SimpleHomeTranslator { // Log private static final Logger APP = Logger.getLogger(SimpleHomeTranslator.class); private Map translatorFields = new HashMap(); private Map translatorMethods = new HashMap(); private SimpleHome home; /** * Constructeur. * * @param resb ResourceBundle contenant la description * @param h Le SimpleHome du translator * * @exception NoSuchFieldException Si il manque un champ * @exception NoSuchMethodException si il manque une methode de traduction */ protected SimpleHomeTranslator(ResourceBundle resb, SimpleHome h) throws NoSuchFieldException, NoSuchMethodException { home = h; for (Enumeration e = resb.getKeys(); e.hasMoreElements();) { String id = (String)e.nextElement(); if (id.startsWith("translator.")) { String propertyName = id.substring(11); StringTokenizer tokenizer = new StringTokenizer(resb.getString(id), "."); String translatorName = tokenizer.nextToken(); String methodName; if (tokenizer.hasMoreTokens()) { methodName = tokenizer.nextToken(); } else { methodName = buildDefautlMethodName(propertyName); } Class translatorClass = addTranslatorFields(propertyName, translatorName); addTranslatorMethod(propertyName, methodName, translatorClass); } } } /** * Traduit la valeur <code>value</code> de la propriete <code>propertyName * </code>avec le traducteur associe (si il existe). Si aucun traducteur n'existe, * la valeur est retourne sans traitement. * * <p> * <b>Remarque</b> : Si <code>value</code> est nulle, le traducteur n'est pas appele. * </p> * * @param propertyName Le nom de la propriete * @param value La valeur de la propriete * * @return La valeur traduite (ou la meme valeur si aucun traducteur) * * @exception PersistenceException Si la traduction echoue */ public Object translateValue(String propertyName, Object value) throws PersistenceException { if (value == null) { return null; } if (translatorFields.containsKey(propertyName) == false) { return value; } try { Method translateMethod = (Method)translatorMethods.get(propertyName); Object[] args = {value}; Object translatedValue = translateMethod.invoke(getTranslator(propertyName), args); return translatedValue; } catch (IllegalAccessException ex) { doTranslateValueTrace(propertyName, value); ex.printStackTrace(); throw new PersistenceException(ex, propertyName + " : Access interdit"); } catch (IllegalArgumentException ex) { doTranslateValueTrace(propertyName, value); ex.printStackTrace(); throw new PersistenceException(ex, propertyName + " : Argument(s) incorrecte"); } catch (InvocationTargetException ex) { doTranslateValueTrace(propertyName, value); ex.printStackTrace(); if (ex.getTargetException() instanceof PersistenceException) { throw (PersistenceException)ex.getTargetException(); } else if (ex.getTargetException() instanceof SQLException) { throw new PersistenceException((SQLException)ex.getTargetException()); } return new PersistenceException(ex, propertyName + " : Erreur inconnue "); } catch (java.lang.NullPointerException ex) { doTranslateValueTrace(propertyName, value); throw new PersistenceException(ex, "Traducteur pour " + propertyName + " non initialise"); } } /** * Retourne le traducteur pour cette property. * * @param propertyName Le nom de la propriete * * @return Le traducteur * * @exception IllegalAccessException Description of Exception */ private Object getTranslator(String propertyName) throws IllegalAccessException { Object translator = translatorFields.get(propertyName); if (translator == null) { translator = home; } else { translator = ((Field)translator).get(home); } return translator; } /** * Realise une trace pour les erreurs lance par la methode translateValue. * * @param propertyName * @param value */ private void doTranslateValueTrace(String propertyName, Object value) { debug(" propertyName = " + propertyName); debug(" value = " + value); debug(" method = " + translatorMethods.get(propertyName)); } /** * Log en mode Debug. * * @param msg message de debug */ private void debug(String msg) { APP.debug(msg); } /** * Ajoute une methode de traduction. * * @param propertyName Le nom de la propriete traduite * @param methodName Le nom de la methode * @param translatorClass La classe du traducteur * * @exception NoSuchMethodException Methode introuvable */ private void addTranslatorMethod(String propertyName, String methodName, Class translatorClass) throws NoSuchMethodException { debug("Recherche methode de traduction : " + methodName + "..."); // Cas par defaut try { Class[] param = {int.class}; Method method = translatorClass.getMethod(methodName, param); translatorMethods.put(propertyName, method); return; } catch (NoSuchMethodException ex) { debug("...Version avec parametre 'int' non trouve..."); } Method[] methods = translatorClass.getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(methodName)) { translatorMethods.put(propertyName, methods[i]); debug("...Methode trouve :" + methods[i]); return; } } throw new NoSuchMethodException(methodName); } /** * Ajoute le champs a la liste. Cette methode comprends le mot clef "this". * * @param propertyName Le nom de la proprietee * @param translatorName Le nom du champs (ou this) * * @return La classe du traducteur * * @exception NoSuchFieldException Description of Exception */ private Class addTranslatorFields(String propertyName, String translatorName) throws NoSuchFieldException { if ("this".equals(translatorName)) { translatorFields.put(propertyName, null); return home.getClass(); } else { Field field = home.getClass().getDeclaredField(translatorName); translatorFields.put(propertyName, field); return field.getType(); } } /** * Construit le nom par defaut de la methode de traduction * * @param propertyName * * @return get[propertyName] */ private static String buildDefautlMethodName(String propertyName) { return "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); } }