/* =========================================================== * TradeManager : An application to trade strategies for the Java(tm) platform * =========================================================== * * (C) Copyright 2011-2011, by Simon Allen and Contributors. * * Project Info: org.trade * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Java is a trademark or registered trademark of Oracle, Inc. * in the United States and other countries.] * * (C) Copyright 2011-2011, by Simon Allen and Contributors. * * Original Author: Simon Allen; * Contributor(s): -; * * Changes * ------- * */ package org.trade.core.message; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.Dictionary; import java.util.Hashtable; import java.util.MissingResourceException; import java.util.PropertyResourceBundle; import java.util.StringTokenizer; import java.util.Vector; import org.trade.core.exception.ExceptionCode; import org.trade.core.exception.ExceptionMessage; import org.trade.core.properties.ConfigProperties; import org.trade.core.properties.PropertyFileNotFoundException; import org.trade.core.properties.PropertyNotFoundException; /** * This class represnets the data and methods necessary to take exception codes * and parameters, and generate the proper exception message based on * information contained in a file. * * <p> * This is the file format: * * <p> * MY_KEY=Invalid name format: the characters[#bad_chars#] are not allowed <br> * MY_KEY_FIELD_REFERENCE=customer_demographic_name <br> * MY_KEY_CODE=NAME0802 * * @author Simon Allen */ public class MessageTranslator { public final static String NAME_OF_MESSAGE_FILE_IN_PROPERTIES = "ERROR_MESSAGE_FILE_NAME"; public final static String CODE_SUFFIX = "_CODE"; public final static String CONTEXT_SUFFIX = ""; // public final static String PARAMETER_SUFFIX = "_PARAMETER_NAME"; public final static String FIELD_REFERENCE_SUFFIX = "_FIELD_REFERENCE"; private static Hashtable<String, MessageFormat> messageFormats = new Hashtable<String, MessageFormat>(); private static Hashtable<String, String[]> indexesTable = new Hashtable<String, String[]>(); private static Hashtable<String, String> fieldReferences = new Hashtable<String, String>(); private static Hashtable<String, String> codes = new Hashtable<String, String>(); // Constants that map to the keys in the property file private PropertyResourceBundle m_props = null; private static MessageTranslator m_theConfig = new MessageTranslator(); // _________________ these methods are taken from the ConfigProperties class // ________ /** * Returns a string for a key. * * @param key * String * @return String * @throws IOException */ public static String getPropAsString(String key) throws IOException { String strRet = null; strRet = m_theConfig._getProperty(key); return strRet; } // read configuration properties /** * Method _getProperty. * * @param key * String * @return String * @throws IOException */ private String _getProperty(String key) throws IOException { String ret = null; if (null == m_props) { InputStream unbuffered; unbuffered = getClass().getResourceAsStream(getPropertyFileName()); if (unbuffered == null) { throw new PropertyFileNotFoundException("Check " + "to see if the property file \"" + getPropertyFileName() + "\" is installed and available in the class path."); } else { InputStream in = new BufferedInputStream(unbuffered); m_props = new PropertyResourceBundle(in); in.close(); unbuffered.close(); } } try { ret = m_props.getString(key); } catch (MissingResourceException e) { throw new PropertyNotFoundException("The property \"" + key + "\" was not found in the property file \"" + getPropertyFileName() + "\". Check the file."); } return ret; } /** * this replaces the same method in the ConfigProperties class. * * @return String */ public static String getPropertyFileName() { try { return ConfigProperties.getPropAsString(NAME_OF_MESSAGE_FILE_IN_PROPERTIES); } catch (Exception e) { // default value return "messages.properties"; } } // ------------------ these methods are not in the ConfigProperties class /** * This method takes an exception code and a dictionary, and it uses the * code to get the message, (actual) code and field reference, if it is an * editcheeck * * @param code * the Exception code that has the code too look up the message * by * @param params * an object that implements the dictionart interface, and * provides the text to fill in the parameters in the message * stored in the file * * @return ExceptionMessage an exception message contatining the proper * code, field reference, and message * @throws * MessageTranslatorException */ public static ExceptionMessage translateExceptionMessage(ExceptionCode code, Dictionary<?, ?> params) throws MessageTranslatorException { return translateExceptionMessage(code.getCode(), params); } /** * This is the same as the translateExceptionMessage method that takes an * exception code, except it takes a string representing the code to use to * look up the message from the file * * @param code * the string to look up the message by * @param params * an object that implements the dictionart interface, and * provides the text to fill in the parameters in the message * stored in the file * * @return ExceptionMessage an exception message contatining the proper * code, field reference, and message * @throws * MessageTranslatorException */ public static ExceptionMessage translateExceptionMessage(String code, Dictionary<?, ?> params) throws MessageTranslatorException { // first look up the message + other info based on the code MessageFormat mf = lookupMessageFormat(code); // this can throw an // exception String[] indexNames = lookupArrayIndexNames(code); String fieldRef = lookupFieldReference(code); String newCode = lookupCodeName(code); // next create an object array from the dictionary Object[] formatParams = new Object[indexNames.length]; for (int i = 0; i < indexNames.length; i++) { Object param = null; if (null != params) { param = params.get(indexNames[i]); } if (null == param) { formatParams[i] = ""; } else { formatParams[i] = param; } } // then create the message String message = mf.format(formatParams); // return an exception message with the gathered values if (fieldRef == null) { return new ExceptionMessage(new ExceptionCode(newCode), message); } else { return new ExceptionMessage(new ExceptionCode(newCode, fieldRef), message); } } /** * Method retrieveExceptionMessage. * * @param index * String * @return ExceptionMessage * @throws MessageTranslatorException */ public static ExceptionMessage retrieveExceptionMessage(String index) throws MessageTranslatorException { String code; String message; String field = null; try { message = getPropAsString(index); code = getPropAsString(index + CODE_SUFFIX); } catch (IOException e) { throw new MessageTranslatorException(e); } try { field = getPropAsString(index + FIELD_REFERENCE_SUFFIX); } catch (PropertyNotFoundException e) { // Ignore since the field is optional } catch (IOException e) { throw new MessageTranslatorException(e); } ExceptionMessage exceptionMessage; exceptionMessage = new ExceptionMessage(new ExceptionCode(code, field), message); return exceptionMessage; } /* * public static ExceptionContext retrieveExceptionContext(String index) * throws MessageTranslatorException { String name; String context; * * try { name = getPropAsString(index + PARAMETER_SUFFIX); context = * getPropAsString(index + CONTEXT_SUFFIX); } catch (IOException e) { throw * new MessageTranslatorException(e); } * * ExceptionContext exceptionContext; exceptionContext = new * ExceptionContext(name, context); * * return exceptionContext; } */ /** * This is the same as the translateExceptionMessage method that takes an * exception code, except it takes an exception message, and extracts the * code from that. because this method takes an exception message as a * parameter, is is able to return the exception message unmodified rather * than throwing an exception * * * @param params * an object that implements the dictionart interface, and * provides the text to fill in the parameters in the message * stored in the file * * @param oldMessage * ExceptionMessage * @return ExceptionMessage an exception message contatining the proper * code, field reference, and message */ public static ExceptionMessage translateExceptionMessage(ExceptionMessage oldMessage, Dictionary<?, ?> params) { try { return translateExceptionMessage(oldMessage.getExceptionCode().getCode(), params); } catch (MessageTranslatorException x) { return oldMessage; } } /** * Method translateExceptionMessage. * * @param code * String * @return ExceptionMessage * @throws MessageTranslatorException */ public static ExceptionMessage translateExceptionMessage(String code) throws MessageTranslatorException { return translateExceptionMessage(code, null); } /** * Method translateExceptionMessage. * * @param code * ExceptionCode * @return ExceptionMessage * @throws MessageTranslatorException */ public static ExceptionMessage translateExceptionMessage(ExceptionCode code) throws MessageTranslatorException { return translateExceptionMessage(code.getCode(), null); } /** * Method translateMessage. * * @param code * String * @param params * Dictionary<?,?> * @return String * @throws MessageTranslatorException */ public static String translateMessage(String code, Dictionary<?, ?> params) throws MessageTranslatorException { MessageFormat mf = lookupMessageFormat(code); // this can throw an // exception String[] indexNames = lookupArrayIndexNames(code); // next create an object array from the dictionary Object[] formatParams = new Object[indexNames.length]; for (int i = 0; i < indexNames.length; i++) { Object param = null; if (null != params) { param = params.get(indexNames[i]); } if (null == param) { formatParams[i] = ""; } else { formatParams[i] = param; } } // then return the message return mf.format(formatParams); } /** * Method translateMessage. * * @param code * String * @return String * @throws MessageTranslatorException */ public static String translateMessage(String code) throws MessageTranslatorException { return translateMessage(code, null); } // look up the message in the hashtable. if it isn't there the try to get // all the info // for that code from the file // if all the info can't be read, throw an exception /** * Method lookupMessageFormat. * * @param code * String * @return MessageFormat * @throws MessageTranslatorException */ private static MessageFormat lookupMessageFormat(String code) throws MessageTranslatorException { MessageFormat mf = messageFormats.get(code); if (mf == null) { loadMessageFormat(code); // this can throw a translator exception mf = messageFormats.get(code); } return mf; } // this is the method that actually carries out the loading of the info for // the given code /** * Method loadMessageFormat. * * @param code * String * @throws MessageTranslatorException */ private static void loadMessageFormat(String code) throws MessageTranslatorException { try { String formatString = getPropAsString(code); // let it throw an // exception if not // found MessageFormat mf = new MessageFormat(formatString); String[] indexes = removeNamesAndCreateIndex(mf); // this also // strips the // named params // and replaces // then with the // numbers that // MessageFormat // uses messageFormats.put(code, mf); indexesTable.put(code, indexes); } catch (Exception x) { throw new MessageTranslatorException(x, x.getMessage()); } } /** * Method loadFieldReference. * * @param code * String */ public static void loadFieldReference(String code) { try { String fieldRef = getPropAsString(code + FIELD_REFERENCE_SUFFIX); // returns // null // if // not // found if (null != fieldRef) { fieldReferences.put(code, fieldRef); } } catch (Exception e) { // it doesn't matter if there isn't a field reference } } /** * Method loadExceptionCode. * * @param code * String * @throws MessageTranslatorException */ public static void loadExceptionCode(String code) throws MessageTranslatorException { try { String newCode = getPropAsString(code + CODE_SUFFIX); // let it // throw an // exception // if not // found if (null != newCode) { codes.put(code, newCode); } else { // there has to be an exception code, otherwise the exception is // not well formed throw new Exception("null value for exception code " + code); } } catch (Exception x) { throw new MessageTranslatorException(x, "unable to load exception code for " + code); } } // looks up the field reference in the hashtable, returning null if the // value is not there /** * Method lookupFieldReference. * * @param code * String * @return String */ private static String lookupFieldReference(String code) { String fieldRef = fieldReferences.get(code); if (null == fieldRef) { loadFieldReference(code); fieldRef = fieldReferences.get(code); } return fieldRef; } // looks up the array index names in the hashtabe for the given code, // returning a zero length // string array if there are none /** * Method lookupArrayIndexNames. * * @param code * String * @return String[] */ private static String[] lookupArrayIndexNames(String code) { String[] toReturn = indexesTable.get(code); if (null == toReturn) { try { loadMessageFormat(code); // this can throw a translator // exception toReturn = indexesTable.get(code); } catch (Exception x) { toReturn = new String[0]; } } return toReturn; } // looks up /** * Method lookupCodeName. * * @param code * String * @return String * @throws MessageTranslatorException */ private static String lookupCodeName(String code) throws MessageTranslatorException { String newCode = codes.get(code); if (null == newCode) { loadExceptionCode(code); newCode = codes.get(code); } return newCode; } // this method takes a message format object that is not valid because it // uses // #name# instead of {0}, {1}, etc. // it replaces each #name# with a number in curly braces // it also creates a string array with all of the names that had to be // removed /** * Method removeNamesAndCreateIndex. * * @param mf * MessageFormat * @return String[] */ private static String[] removeNamesAndCreateIndex(MessageFormat mf) { // todo: mabe optimize this to not create a new array each time // this code is pasted from the database connection class StringTokenizer tokenizer = new StringTokenizer(mf.toPattern(), "#"); int nbrTokens = tokenizer.countTokens(); int counter = 0; Vector<String> returnVector = new Vector<String>(); StringBuffer buf = new StringBuffer(); for (int i = 0; i < nbrTokens; i++) // while (nbrTokens-- > 0) { String token = tokenizer.nextToken(); if ((i % 2) == 0) // This is not a parameter. { // no index name to add to the array, nothig to replace in the // string buf.append(token); } else // We have a parameter. { returnVector.addElement(token); // Remove the parameter markup and replace it with a number // inside curly // braces for the MessageFormat to replace with paramaters buf.append("{" + counter + "}"); counter++; } } mf.applyPattern(buf.toString()); if (returnVector.size() >= 1) { String[] namedIndexes = new String[returnVector.size()]; returnVector.copyInto(namedIndexes); return namedIndexes; } else { return new String[0]; } } }