001package jmri.util.swing; 002 003import javax.annotation.Nonnull; 004 005import jmri.jmrit.mailreport.ReportContext; 006import jmri.util.StringUtil; 007 008/** 009 * Wraps an Exception and allows extra contextual information to be added, such 010 * as what was happening at the time of the Exception, and a hint as to what the 011 * user might do to correct the problem. Also implements a number of methods to 012 * format the data of an Exception. 013 * 014 * @author Gregory Madsen Copyright (C) 2012 015 * 016 */ 017public class ExceptionContext { 018 019 protected final Throwable exception; 020 protected String prefaceString = "An error occurred during the following operation."; // NOI18N 021 protected String operation; 022 protected String hint = ""; 023 024 /** 025 * Create a new Exception Context. 026 * @param ex the Throwable Exception which has occurred. 027 * @param operation An Operation which was taking place at the time of the 028 * Exception. Use empty String if unknown. 029 * @param hint A hint as to what might have caused the Exception. 030 * Use empty String if unknown. 031 */ 032 public ExceptionContext(@Nonnull Throwable ex, @Nonnull String operation, @Nonnull String hint) { 033 this.exception = ex; 034 this.operation = operation; 035 this.hint = hint; 036 } 037 038 /** 039 * Get the Exception being wrapped. 040 * @return the Exception. 041 */ 042 public Throwable getException() { 043 return exception; 044 } 045 046 /** 047 * Used to give a more friendly message. 048 * @return the Preface String. 049 */ 050 public String getPreface() { 051 return prefaceString; 052 } 053 054 /** 055 * Get what was happening when the exception occurred. 056 * @return empty String if unknown. 057 */ 058 public String getOperation() { 059 return operation; 060 } 061 062 /** 063 * Get suggestion to the user to correct the problem. 064 * @return empty string if no hint, else the hint text. 065 */ 066 @Nonnull 067 public String getHint() { 068 return hint; 069 } 070 071 /** 072 * Get a String to use as the Title for this Context. 073 * @return Localised Exception message, truncated to 80 chars. 074 */ 075 public String getTitle() { 076 String msg = exception.getLocalizedMessage(); 077 return msg.substring(0, Math.min(msg.length(), 80)); 078 } 079 080 /** 081 * Returns a user friendly summary of the Exception. 082 * Empty data is excluded. 083 * @return A string summary. 084 */ 085 public String getSummary() { 086 StringBuilder sb = new StringBuilder(); 087 if (!getPreface().isBlank()) { 088 sb.append(getPreface()) 089 .append(System.lineSeparator()).append(System.lineSeparator()); 090 } 091 092 if (!getOperation().isBlank()) { 093 sb.append(getOperation()).append(System.lineSeparator()); 094 } 095 096 if (!getHint().isBlank()) { 097 sb.append(getHint()).append(System.lineSeparator()); 098 } 099 100 if (!getException().getMessage().equals(getException().getLocalizedMessage())) { 101 sb.append(getException().getLocalizedMessage()).append(System.lineSeparator()); 102 } 103 104 sb.append(getException().getMessage()).append(System.lineSeparator()); 105 sb.append(getException().getClass().getName()).append(System.lineSeparator()); 106 107 Throwable cause = getException().getCause(); 108 if (cause != null) { 109 sb.append(cause.toString()).append(System.lineSeparator()); 110 } 111 sb.append(getException().toString()); 112 return StringUtil.stripHtmlTags(sb.toString()); 113 } 114 115 /** 116 * Returns up to the given number of stack trace elements concatenated into 117 * one string. 118 * @param maxLevels The number of stack trace elements to return. 119 * @return A string stack trace. 120 * 121 */ 122 public String getStackTraceAsString(int maxLevels) { 123 StringBuilder sb = new StringBuilder(); 124 125 StackTraceElement[] stElements = exception.getStackTrace(); 126 127 int limit = Math.min(maxLevels, stElements.length); 128 for (int i = 0; i < limit; i++) { 129 sb.append(" at "); // NOI18N 130 sb.append(stElements[i].toString()); 131 sb.append(System.lineSeparator()); 132 } 133 134 // If there are more levels than included, add a note to the end 135 if (stElements.length > limit) { 136 sb.append(" plus "); // NOI18N 137 sb.append(stElements.length - limit); 138 sb.append(" more."); // NOI18N 139 } 140 return sb.toString(); 141 } 142 143 /** 144 * Get the Full Stack Trace String. 145 * @return unabridged Stack Trace String. 146 */ 147 public String getStackTraceString() { 148 return getStackTraceAsString(Integer.MAX_VALUE); 149 } 150 151 /** 152 * Get a String form of this Context for use in pasting to Clipboard. 153 * @param includeSysInfo true to include System Information, 154 * false for just the Exception details. 155 * @return String for use in Clipboard text. 156 */ 157 public String getClipboardString(boolean includeSysInfo){ 158 StringBuilder sb = new StringBuilder(); 159 sb.append(getSummary()); 160 sb.append(System.lineSeparator()); 161 sb.append(getStackTraceString()); 162 if ( includeSysInfo ) { 163 sb.append(System.lineSeparator()); 164 sb.append(new ReportContext().getReport(true)); 165 } 166 return sb.toString(); 167 } 168 169}