En la secci�n, Manejo de Errores utilizando Excepciones, mostramos como lanzar y capturar excepciones. Como podr�s recordar, los mensajes de excepciones mostrados por los ejemplos de esta lecci�n est�n en Ingl�s. Frecuentemente, los mensajes de excepciones s�lo se utilizan para prop�sitos de depurado y nunca son vistos por lo usuarios finales. Sin embargo, si estos usuarios est�n expuestos a mensajes de excepciones, deberemos asegurarnos de que no est�n codificados en cualquier otro idioma.
En esta lecci�n, daremos algunos trucos para hacer los mensajes de excepciones independientes de la Localidad. Los ejemplos de c�digo de esta secci�n incluyen objetos ResourceBundle y MessageFormat. Si no lo has hecho anteriormente, deber�as leer primero Aislar Objetos Espec�ficos de la Localidad en un ResourceBundle, y Formateo de Mensajes.
�Manejar Mensajes de Excepciones Codificados
Si el texto del mensaje de un subclase de Exception ha sido codificado, no puede ser localizado, Pero existe un atajo, que explicaremos en esta secci�n.
Supongamos que llamamos a un m�todo que lanza una excepci�n, y queremos mostrar el mensaje de la excepci�n cuando ocurra el error. Si este m�todo pertenece a una clase de un paquete escrito por otras personas, no tendremos ning�n control sobre el texto del mensaje a menos que tengamos acceso al c�digo fuente. Este puede ser un grave problema si el texto del mensaje ha sido codificado, porque no podr� ser traducido a otros idiomas. En el siguiente c�digo de ejemplo, encontramos este problema cuando abrimos un fichero que no existe.
static public void defaultMessage() { try { FileInputStream in = new FileInputStream("vapor.txt"); } catch (FileNotFoundException e) { System.out.println("e.getMessage = " + e.getMessage()); System.out.println("e.getLocalizedMessage = " + e.getLocalizedMessage()); System.out.println("e.toString = " + e.toString()); } }
La clausula Catch del m�todo defaultMessage genera la siguiente salida.
e.getMessage = vapor.txt e.getLocalizedMessage = vapor.txt e.toString = java.io.FileNotFoundException: vapor.txt
Estos mensajes son inadecuados por varias razones. Primero, el texto del mensaje est� codificado dentro de la clase FileNotFoundException y no puede ser localizado. Intentamos imprimir el String devuelto por getLocalizedMessage, pero como getLocalizedMessage no ha sido implementada, s�lo devuelve el mismo mensaje que el m�todo getMessage. Adem�s, getMessage s�lo devuelve el nombre del fichero, que por s� mismo no es un mensaje traducible. Aunque podr�amos encontrar �til el tener el nombre del fichero durante la depuraci�n, nuestros usuarios finales necesitar�n un mensaje m�s informativo.
En general, los mensajes proporcionados por subclases de Exception, como FileNotFoundException, se han creado para programadores, no para usuarios finales. Por ejemplo, un procesador de textos no deber�a mostrar, "java.io.FileNotFoundException: vapor.txt," cuando el usuario trata de abrir un fichero que no existe. Este mensaje, aunque es t�cnicamente correcto, no tiene sentido para la mayor�a de la gente. En su lugar, cuando el procesador de textos capturara la FileNotFoundException, deber�a mostrar este mensaje: "Cannot find the file named vapor.txt. Make sure the file exists."
En una aplicaci�n como un procesador de textos, el texto devuelto por FileNotFoundException.getMessage no es apropiado para los usuarios finales. Deber�amos proporcionar un mensaje que sea f�cil de entender, y que pueda ser traducido. Veremos como hacer esto con el siguiente ejemplo.
Como el texto del mensaje necesita se traducido, lo aislaremos en un ResourceBundle. Almacenaremos nuestros mensajes en un fichero de propiedades, construiremos un ResourceBundle llamado ExceptionBundle. Aqu� tenemos el mensaje del fichero ExceptionBundle_en_US.properties.
template = Cannot find the file named {0}. Make sure the file exists.
En el siguiente c�digo mostramos un mensaje sensible a la localidad cuando se captura FileNotFoundException. Primero creamos un ResourceBundle para la Localidad apropiada. Luego recuperamos el patr�n del mensaje traducido desde el ResourceBundle. Finalmente, aplicamos el patr�n del formato para insertar el nombre del fichero en el mensaje.
static public void customMessage(Locale currentLocale) { System.out.println("Locale: " + currentLocale.toString()); String fileName = "vapor.txt"; try { FileInputStream in = new FileInputStream(fileName); } catch (FileNotFoundException e) { ResourceBundle messages = ResourceBundle.getBundle("ExceptionBundle",currentLocale); Object[] messageArguments = {fileName}; MessageFormat formatter = new MessageFormat(""); formatter.setLocale(currentLocale); formatter.applyPattern(messages.getString("template")); String errorOut = formatter.format(messageArguments); System.out.println(errorOut); } }
La salida del m�todo customMessage est� bastante mejor que: "java.io.FileNotFoundException: vapor.txt."
Locale: en_US Cannot find the file named vapor.txt. Make sure the file exists. Locale: de_DE Die Datei vapor.txt konnte nicht gefunden werden. Stellen Sie sicher, da� die Datei existiert.
�Crear Subclases de Exception Independientes de la Localidad
Si creamos una subclase de Exception, deber�amos asegurarnos de que no contiene un mensajes codificado en su interior. Nuestras subclases, deber�an implementar el m�todo getLocalizedMessage.
Si queremos crear un subclase de Exception cuando necesitamos manejar errores espec�ficos de nuestras aplicaciones, podemos crear nuestras subclases independientes de la Localidad sobrescribiendo su m�todo getLocalizedMessage. Cada m�todo que sobrescribamos deber�a aislar el mensaje que devuelve en un ResourceBundle. Esto permitir� que el mensaje sea traducido a varios idiomas durante la localizaci�n.
En el siguiente ejemplo, veremos c�mo crear una subclase de Exception que muestra un mensaje independiente de la Localidad. El c�digo fuente de este programa est� en estos dos ficheros. OverLimitException.java y Account.java.
El programa Account simula una cuenta de cr�dito. Si este programa se excede el l�mite de cr�dito, recupera un mensaje localizado desde un ResourceBundle llamado ExceptionBundle, y luego muestra el mensaje. En el fichero ExceptionBundle_en_US.properties, hemos especificado este mensaje.
pattern = Negative balance of {0,number,currency} is not allowed.
El programa Account recupera el mensaje con el m�todo getLocalizedMessage, que hemos implementado en una subclase de Exception llamada OverLimitException. El m�todo getLocalizedMessage acepta un objeto Locale como par�metro. Especificamos la Localidad cuando recuperamos el patr�n del mensaje desde el ResourceBundle, y tambi�n cuando definimos el objeto MessageFormat. Por lo tanto, nuestra clase OverLimitException es sensible a la Localidad.
public class OverLimitException extends Exception { private double detail; public OverLimitException (double amount) { detail = amount; } public String getMessage() { return getLocalizedMessage(Locale.getDefault()); } public String getLocalizedMessage(Locale currentLocale) { ResourceBundle messages = ResourceBundle.getBundle("ExceptionBundle",currentLocale); Object[] messageArguments = {new Float(detail)}; MessageFormat formatter = new MessageFormat(""); formatter.setLocale(currentLocale); formatter.applyPattern(messages.getString("pattern")); return formatter.format(messageArguments); } }
En la clase Account, el m�todo withdraw lanza un OverLimitException si el nuevo balance excede el l�mite de cr�dito. En el siguiente c�digo, observa que hemos pasado el par�metro value al constructor OverLimitException en la sentencia throw. El constructor asigna este par�metro al campo detail de la clase OverLimitException. El m�todo getLocalizedMessage inserta una representaci�n String del campo detail en el mensaje devuelto. El m�todo withdraw es el siguiente.
public void withdraw(double amount) throws OverLimitException { double value = balance - amount; if (value < creditLimit) { throw new OverLimitException(value); } else { balance = value; } }
En el m�todo main de la clase Account, llamamos al m�todo withdraw y captura el OverLimitException siempre que se lance. La clausula catch imprime el String devuelto por getLocalizedMessage. Aqu� tienes el m�todo main.
static public void main(String[] args) { Locale[] locales = { new Locale("en","US"), new Locale("de","DE") }; Account credit = new Account(); credit.deposit(20.00f); for (int k = 0; k < locales.length; k++) { try { credit.withdraw(1000.00f); } catch (OverLimitException e) { System.out.println("Locale: " + locales[k].toString()); System.out.println(e.getLocalizedMessage(locales[k])); } } // for }
El programa Account muestra dos mensajes localizados. No s�lo el texto se muestra en el idioma correcto, si no que tambi�n el formato de moneda es el adecuado para cada Localidad..
% java Account Locale: en_US Negative balance of ($980.00) is not allowed. Locale: de_DE Ihr Konto um -980,00 DM zu �berziehen ist nicht gestattet.