001    package org.springframework.richclient.exceptionhandling;
002    
003    import org.apache.commons.lang.StringUtils;
004    import org.apache.commons.lang.WordUtils;
005    import org.springframework.context.support.DefaultMessageSourceResolvable;
006    import org.springframework.core.ErrorCoded;
007    
008    import java.util.ArrayList;
009    import java.util.List;
010    import java.util.StringTokenizer;
011    import java.sql.SQLException;
012    
013    /**
014     * Displays a message to the user which is fetched from the I18N files
015     * based on the class and superclasses of the throwable.
016     * <p/>
017     * For example if an IllegalArgumentException is thrown, it will search for
018     * java.lang.IllegalArgumentException.caption and java.lang.IllegalArgumentException.description first,
019     * and if it cant find that it will try in order:
020     * java.lang.RuntimeException.caption/description, java.lang.Exception.caption/description and
021     * java.lang.Throwable.caption/description.
022     * <p/>
023     * The exception message is passed as a parameter, but is idented and wrapped first.
024     * Note for the repacing of {0} to work in a property file double quotes(") need to be escaped (\")
025     * and that single quotes (') should be avoided (escaping doesn't seem to work).
026     * @author Geoffrey De Smet
027     * @since 0.3
028     */
029    public class MessagesDialogExceptionHandler extends AbstractDialogExceptionHandler {
030    
031        private int wrapLength = 120;
032        private int identLength = 2;
033        
034        private String messagesKey = null;
035    
036        /**
037         * Sets the wrap length applied on the exception message passed as a parameter.
038         * Defaults to 120.
039         * @param wrapLength
040         */
041        public void setWrapLength(int wrapLength) {
042            this.wrapLength = wrapLength;
043        }
044    
045        /**
046         * Sets the identation applied on the exception message passed as a parameter.
047         * Defaults to 2.
048         * @param identLength
049         */
050        public void setIdentLength(int identLength) {
051            this.identLength = identLength;
052        }
053    
054        /**
055         * If messagesKey is set, the caption and description shown in the dialog
056         * are not based dynamically on the throwable,
057         * but instead statically on the keys messageKey.caption and messageKey.description.
058         * 
059         * @param messagesKey the key used for the caption and title
060         */
061        public void setMessagesKey(String messagesKey) {
062            this.messagesKey = messagesKey;
063        }
064    
065        
066        public String resolveExceptionCaption(Throwable throwable) {
067            String[] messagesKeys = getMessagesKeys(throwable, ".caption");
068            return messageSourceAccessor.getMessage(new DefaultMessageSourceResolvable(
069                    messagesKeys, messagesKeys[0]));
070        }
071    
072        public Object createExceptionContent(Throwable throwable) {
073            String[] messagesKeys = getMessagesKeys(throwable, ".description");
074            String[] parameters = new String[]{formatMessage(throwable.getMessage())};
075            return messageSourceAccessor.getMessage(new DefaultMessageSourceResolvable(
076                    messagesKeys, parameters, messagesKeys[0]));
077        }
078    
079        private String[] getMessagesKeys(Throwable throwable, String keySuffix) {
080            if (messagesKey != null) {
081                return new String[] {messagesKey};
082            }
083            List<String> messageKeyList = new ArrayList<String>();
084            Class clazz = throwable.getClass();
085            if(throwable instanceof ErrorCoded)
086            {
087                messageKeyList.add(((ErrorCoded) throwable).getErrorCode() +  keySuffix);
088            }
089            if(throwable instanceof SQLException)
090            {
091                messageKeyList.add(SQLException.class.getName() + "." + ((SQLException) throwable).getErrorCode() + keySuffix);
092            }
093            while (clazz != Object.class) {
094                messageKeyList.add(clazz.getName() + keySuffix);
095                clazz = clazz.getSuperclass();
096            }
097            return messageKeyList.toArray(new String[messageKeyList.size()]);
098        }
099    
100        protected String formatMessage(String message) {
101            if (message == null) {
102                return "";
103            }
104            String identString = StringUtils.leftPad("", identLength);
105            String newLineWithIdentString = "\n" + identString;
106            StringBuilder formattedMessageBuilder = new StringBuilder(identString);
107            StringTokenizer messageTokenizer = new StringTokenizer(message, "\n");
108            while (messageTokenizer.hasMoreTokens()) {
109                String messageToken = messageTokenizer.nextToken();
110                formattedMessageBuilder.append(WordUtils.wrap(messageToken, wrapLength, newLineWithIdentString, true));
111                if (messageTokenizer.hasMoreTokens()) {
112                    formattedMessageBuilder.append(newLineWithIdentString);
113                }
114            }
115            return formattedMessageBuilder.toString();
116        }
117    
118    }