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 String msg = getException().getMessage(); 101 if ( msg != null && !msg.equals(getException().getLocalizedMessage())) { 102 sb.append(getException().getLocalizedMessage()).append(System.lineSeparator()); 103 } 104 105 sb.append(getException().getMessage()).append(System.lineSeparator()); 106 sb.append(getException().getClass().getName()).append(System.lineSeparator()); 107 108 Throwable cause = getException().getCause(); 109 if (cause != null) { 110 sb.append(cause.toString()).append(System.lineSeparator()); 111 } 112 sb.append(getException().toString()); 113 return StringUtil.stripHtmlTags(sb.toString()); 114 } 115 116 /** 117 * Returns up to the given number of stack trace elements concatenated into 118 * one string. 119 * @param maxLevels The number of stack trace elements to return. 120 * @return A string stack trace. 121 * 122 */ 123 public String getStackTraceAsString(int maxLevels) { 124 StringBuilder sb = new StringBuilder(); 125 126 StackTraceElement[] stElements = exception.getStackTrace(); 127 128 int limit = Math.min(maxLevels, stElements.length); 129 for (int i = 0; i < limit; i++) { 130 sb.append(" at "); // NOI18N 131 sb.append(stElements[i].toString()); 132 sb.append(System.lineSeparator()); 133 } 134 135 // If there are more levels than included, add a note to the end 136 if (stElements.length > limit) { 137 sb.append(" plus "); // NOI18N 138 sb.append(stElements.length - limit); 139 sb.append(" more."); // NOI18N 140 } 141 return sb.toString(); 142 } 143 144 /** 145 * Get the Full Stack Trace String. 146 * @return unabridged Stack Trace String. 147 */ 148 public String getStackTraceString() { 149 return getStackTraceAsString(Integer.MAX_VALUE); 150 } 151 152 /** 153 * Get a String form of this Context for use in pasting to Clipboard. 154 * @param includeSysInfo true to include System Information, 155 * false for just the Exception details. 156 * @return String for use in Clipboard text. 157 */ 158 public String getClipboardString(boolean includeSysInfo){ 159 StringBuilder sb = new StringBuilder(); 160 sb.append(getSummary()); 161 sb.append(System.lineSeparator()); 162 sb.append(getStackTraceString()); 163 if ( includeSysInfo ) { 164 sb.append(System.lineSeparator()); 165 sb.append(new ReportContext().getReport(true)); 166 } 167 return sb.toString(); 168 } 169 170}