create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / logging / TraceException.java
1 package net.sf.openrocket.logging;
2
3
4
5 /**
6  * An exception that is used to store a stack trace.  On modern computers
7  * instantiation of an exception takes on the order of one microsecond, while
8  * examining the trace typically takes several times longer.  Therefore the
9  * exception should be stored and the stack trace examined only when necessary.
10  * <p>
11  * The {@link #getMessage()} method returns a description of the position
12  * where this exception has been instantiated.  The position is provided
13  * as many levels upwards from the instantiation position as provided to the
14  * constructor.
15  * 
16  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
17  */
18 public class TraceException extends Exception {
19         
20         private static final String STANDARD_PACKAGE_PREFIX = "net.sf.openrocket.";
21         
22         private final int minLevel;
23         private final int maxLevel;
24         private volatile String message = null;
25         
26         
27         /**
28          * Instantiate exception that provides the line of instantiation as a message.
29          */
30         public TraceException() {
31                 this(0, 0);
32         }
33         
34         /**
35          * Instantiate exception that provides the provided number of levels upward
36          * from the instantiation location as a message.  The level provided 
37          * is how many levels upward should be examined to find the stack trace 
38          * position for the exception message.
39          * 
40          * @param level         how many levels upward to examine the stack trace to find
41          *                                      the correct message.
42          */
43         public TraceException(int level) {
44                 this(level, level);
45         }
46         
47         
48         /**
49          * Instantiate exception that provides a range of levels upward from the
50          * instantiation location as a message.  This is useful to identify the
51          * next level of callers upward.
52          * 
53          * @param minLevel      the first level which to include.
54          * @param maxLevel      the last level which to include.
55          */
56         public TraceException(int minLevel, int maxLevel) {
57                 if (minLevel > maxLevel || minLevel < 0) {
58                         throw new IllegalArgumentException("minLevel=" + minLevel + " maxLevel=" + maxLevel);
59                 }
60                 this.minLevel = minLevel;
61                 this.maxLevel = maxLevel;
62         }
63         
64         
65         /**
66          * Construct an exception with the specified message.
67          * 
68          * @param message       the message for the exception.
69          */
70         public TraceException(String message) {
71                 this(0, 0);
72                 this.message = message;
73         }
74         
75         
76         /**
77          * Construct an exception with the specified message and cause.
78          * 
79          * @param message       the message for the exception.
80          * @param cause         the cause for this exception.
81          */
82         public TraceException(String message, Throwable cause) {
83                 this(0, 0);
84                 this.message = message;
85                 this.initCause(cause);
86         }
87         
88         
89         /**
90          * Get the description of the code position as provided in the constructor.
91          */
92         @Override
93         public String getMessage() {
94                 if (message == null) {
95                         StackTraceElement[] elements = this.getStackTrace();
96                         
97                         StringBuilder sb = new StringBuilder();
98                         sb.append('(');
99                         
100                         if (elements == null || elements.length == 0) {
101                                 sb.append("no stack trace");
102                         } else {
103                                 
104                                 int levelCount = 0;
105                                 int position = minLevel;
106                                 while (levelCount <= (maxLevel - minLevel) && position < elements.length) {
107                                         
108                                         // Ignore synthetic "access$0" methods generated by the JRE
109                                         if (elements[position].getMethodName().contains("$")) {
110                                                 position++;
111                                                 continue;
112                                         }
113                                         
114                                         if (levelCount > 0) {
115                                                 sb.append(' ');
116                                         }
117                                         sb.append(toString(elements[position]));
118                                         levelCount++;
119                                         position++;
120                                 }
121                                 
122                         }
123                         sb.append(')');
124                         
125                         message = sb.toString();
126                 }
127                 return message;
128         }
129         
130         
131         private static String toString(StackTraceElement element) {
132                 if (element.getClassName().startsWith(STANDARD_PACKAGE_PREFIX)) {
133                         return element.getFileName() + ":" + element.getLineNumber();
134                 } else {
135                         return element.toString();
136                 }
137         }
138         
139 }