001 /*
002 * $Id: StackTraceScanner.java,v 1.5 2014/05/17 17:31:33 oboehm Exp $
003 *
004 * Copyright (c) 2014 by Oliver Boehm
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 * (c)reated 24.01.2014 by oliver (ob@oasd.de)
019 */
020
021 package patterntesting.runtime.util;
022
023 import java.lang.reflect.Method;
024 import java.util.regex.Pattern;
025
026 import org.apache.commons.lang.ArrayUtils;
027 import org.aspectj.lang.Signature;
028 import org.aspectj.lang.reflect.*;
029 import org.slf4j.*;
030
031 import patterntesting.runtime.exception.NotFoundException;
032
033 /**
034 * This class allows you to scan the stacktrace for different stuff.
035 *
036 * @author oliver
037 * @since 1.4.1 (24.01.2014)
038 */
039 public final class StackTraceScanner {
040
041 private static final Logger log = LoggerFactory.getLogger(StackTraceScanner.class);
042
043 /** Utility class - no need to instantiate it. */
044 private StackTraceScanner() {}
045
046 /**
047 * Find the constructor of the given class on the stack trace.
048 *
049 * @param clazz the clazz
050 * @return the stack trace element
051 */
052 public static StackTraceElement findConstructor(final Class<?> clazz) {
053 return find (clazz.getName(), "<init>");
054 }
055
056 /**
057 * Find the given signature on the stack trace.
058 *
059 * @param signature the signature
060 * @return the stack trace element
061 */
062 public static StackTraceElement find(final Signature signature) {
063 if (signature instanceof MethodSignature) {
064 return find((MethodSignature) signature);
065 } else if (signature instanceof ConstructorSignature) {
066 return find((ConstructorSignature) signature);
067 } else {
068 throw new IllegalArgumentException(signature + " is not a method or ctor signature");
069 }
070 }
071
072 private static StackTraceElement find(final ConstructorSignature signature) {
073 return findConstructor(signature.getDeclaringType());
074 }
075
076 private static StackTraceElement find(final MethodSignature signature) {
077 return find(signature.getDeclaringType(), signature.getMethod());
078 }
079
080 /**
081 * Find the given method on the stack trace.
082 *
083 * @param clazz the clazz
084 * @param method the method
085 * @return the stack trace element
086 */
087 public static StackTraceElement find(final Class<?> clazz, final Method method) {
088 return find (clazz.getName(), method.getName());
089 }
090
091 /**
092 * Find the given method on the stack trace.
093 *
094 * @param clazz the clazz
095 * @param methodName the method name
096 * @return the stack trace element
097 */
098 public static StackTraceElement find(final Class<?> clazz, final String methodName) {
099 return find (clazz.getName(), methodName);
100 }
101
102 /**
103 * Find the given method on the stack trace.
104 *
105 * @param classname the classname
106 * @param methodName the method name
107 * @return the stack trace element
108 */
109 public static StackTraceElement find(final String classname, final String methodName) {
110 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
111 int i = getIndexOf(classname, methodName, stackTrace);
112 return stackTrace[i];
113 }
114
115 /**
116 * Gets the caller of a constructor.
117 *
118 * @param clazz the clazz
119 * @return the caller of constructor
120 */
121 public static StackTraceElement getCallerOfConstructor(final Class<?> clazz) {
122 return getCallerOfConstructor(clazz, new Pattern[0]);
123 }
124
125 /**
126 * Gets the caller of a constructor.
127 *
128 * @param clazz the clazz
129 * @param excluded the excluded
130 * @return the caller of constructor
131 */
132 public static StackTraceElement getCallerOfConstructor(final Class<?> clazz, final Pattern...excluded) {
133 try {
134 return getCallerOf(clazz, "<init>", excluded);
135 } catch (NotFoundException nfe) {
136 log.debug("Looking for adviced init method because of {}", nfe.getMessage());
137 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
138 for (int i = 1; i < stackTrace.length; i++) {
139 if (stackTrace[i].getMethodName().startsWith("init$")) {
140 for (i++; i < stackTrace.length; i++) {
141 if (!matches(stackTrace[i].getMethodName(), excluded)) {
142 return stackTrace[i];
143 }
144 }
145 }
146 }
147 throw new NotFoundException("new " + clazz.getSimpleName() + "(..) not part of " + stackTrace);
148 }
149 }
150
151 /**
152 * Gets the caller of the given signature.
153 *
154 * @param signature the signature
155 * @return the caller of
156 */
157 public static StackTraceElement getCallerOf(final Signature signature) {
158 return getCallerOf(signature, new Pattern[0]);
159 }
160
161 /**
162 * Gets the caller of the given method.
163 *
164 * @param clazz the clazz
165 * @param method the method
166 * @return the caller of
167 */
168 public static StackTraceElement getCallerOf(final Class<?> clazz, final Method method) {
169 return getCallerOf(clazz, method, new Pattern[0]);
170 }
171
172 /**
173 * Gets the caller of the given method.
174 *
175 * @param clazz the clazz
176 * @param methodName the method name
177 * @return the caller of
178 */
179 public static StackTraceElement getCallerOf(final Class<?> clazz, final String methodName) {
180 return getCallerOf(clazz, methodName, new Pattern[0]);
181 }
182
183 /**
184 * Gets the caller of the given method.
185 *
186 * @param classname the classname
187 * @param methodName the method name
188 * @return the caller of
189 */
190 public static StackTraceElement getCallerOf(final String classname, final String methodName) {
191 return getCallerOf(classname, methodName, new Pattern[0]);
192 }
193
194 /**
195 * Gets the caller of the given signature.
196 *
197 * @param signature the signature
198 * @param excluded the excluded
199 * @return the caller of
200 */
201 public static StackTraceElement getCallerOf(final Signature signature, final Pattern... excluded) {
202 if (signature instanceof MethodSignature) {
203 return getCallerOf((MethodSignature) signature, excluded);
204 } else if (signature instanceof ConstructorSignature) {
205 return getCallerOf((ConstructorSignature) signature, excluded);
206 } else {
207 throw new IllegalArgumentException(signature + " is not a method or ctor signature");
208 }
209 }
210
211 private static StackTraceElement getCallerOf(final ConstructorSignature signature,
212 final Pattern... excluded) {
213 return getCallerOfConstructor(signature.getDeclaringType(), excluded);
214 }
215
216 private static StackTraceElement getCallerOf(final MethodSignature signature,
217 final Pattern... excluded) {
218 return getCallerOf(signature.getDeclaringType(), signature.getMethod(), excluded);
219 }
220
221 /**
222 * Gets the caller of the given method.
223 *
224 * @param clazz the clazz
225 * @param method the method
226 * @param excluded a list of filters which should be not considered as
227 * caller
228 * @return the caller of
229 */
230 public static StackTraceElement getCallerOf(final Class<?> clazz, final Method method, final Pattern... excluded) {
231 return getCallerOf(clazz.getName(), method.getName(), excluded);
232 }
233
234 /**
235 * Gets the caller of the given method.
236 *
237 * @param clazz the clazz
238 * @param methodName the method name
239 * @param excluded a list of filters which should be not considered as
240 * caller
241 * @return the caller of
242 */
243 public static StackTraceElement getCallerOf(final Class<?> clazz, final String methodName, final Pattern... excluded) {
244 return getCallerOf(clazz.getName(), methodName, excluded);
245 }
246
247 /**
248 * Gets the caller of the given method.
249 *
250 * @param classname the classname
251 * @param methodName the method name
252 * @param excluded a list of filters which should be not considered as
253 * caller
254 * @return the caller of
255 */
256 public static StackTraceElement getCallerOf(final String classname, final String methodName, final Pattern... excluded) {
257 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
258 for (int i = getIndexOf(classname, methodName, stackTrace) + 1; i < stackTrace.length; i++) {
259 if (!matches(stackTrace[i].getMethodName(), excluded)) {
260 return stackTrace[i];
261 }
262 }
263 throw new NotFoundException(classname + "." + methodName +"()");
264 }
265
266 private static int getIndexOf(final String classname, final String methodName,
267 final StackTraceElement[] stackTrace) {
268 for (int i = 1; i < stackTrace.length; i++) {
269 StackTraceElement element = stackTrace[i];
270 if (methodName.equals(element.getMethodName())
271 && classname.equals(element.getClassName())) {
272 return i;
273 }
274 }
275 throw new NotFoundException(classname + "." + methodName +"()");
276 }
277
278 private static boolean matches(final String methodName, final Pattern... excluded) {
279 for (int j = 0; j < excluded.length; j++) {
280 if (excluded[j].matcher(methodName).matches()) {
281 return true;
282 }
283 }
284 return false;
285 }
286
287 private static boolean matches(final String className, final Class<?>... excluded) {
288 for (int j = 0; j < excluded.length; j++) {
289 if (className.equals(excluded[j].getName())) {
290 return true;
291 }
292 }
293 return false;
294 }
295
296 /**
297 * Gets the caller class by examing the stacktrace.
298 *
299 * @return the caller class
300 */
301 public static Class<?> getCallerClass() {
302 return getCallerClass(new Pattern[0]);
303 }
304
305 /**
306 * Gets the caller class by examing the stacktrace.
307 *
308 * @param excluded a list of filters which should be not considered as
309 * caller
310 * @return the caller of
311 */
312 public static Class<?> getCallerClass(final Pattern... excluded) {
313 return getCallerClass(excluded, new Class<?>[0]);
314 }
315
316 /**
317 * Gets the caller class by examing the stacktrace.
318 *
319 * @param excludedMethods the excluded methods
320 * @param excludedClasses the excluded classes
321 * @return the caller class
322 */
323 public static Class<?> getCallerClass(final Pattern[] excludedMethods, final Class<?>...excludedClasses) {
324 StackTraceElement[] stackTrace = getCallerStackTrace(excludedMethods, excludedClasses);
325 String classname = stackTrace[0].getClassName();
326 try {
327 return Class.forName(classname);
328 } catch (ClassNotFoundException ex) {
329 throw new NotFoundException(classname, ex);
330 }
331 }
332
333 /**
334 * Gets the caller stack trace of the method or constructor which calls it.
335 *
336 * @return the caller stack trace
337 * @since 1.4.2 (17.05.2014)
338 */
339 public static StackTraceElement[] getCallerStackTrace() {
340 return getCallerStackTrace(new Pattern[0]);
341 }
342
343 /**
344 * Gets the caller stack trace of the method or constructor which calls it.
345 *
346 * @param excluded a list of filters which should be not considered as
347 * caller
348 * @return the caller stack trace
349 * @since 1.4.2 (17.05.2014)
350 */
351 public static StackTraceElement[] getCallerStackTrace(final Pattern... excluded) {
352 return getCallerStackTrace(excluded, new Class<?>[0]);
353 }
354
355 /**
356 * Gets the caller stack trace of the method or constructor which calls it.
357 *
358 * @param excludedMethods the excluded methods
359 * @param excludedClasses the excluded classes
360 * @return the caller stack trace
361 * @since 1.4.2 (17.05.2014)
362 */
363 public static StackTraceElement[] getCallerStackTrace(final Pattern[] excludedMethods, final Class<?>...excludedClasses) {
364 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
365 int i = 2;
366 String scannerClassName = StackTraceScanner.class.getName();
367 for (; i < stackTrace.length-1; i++) {
368 if (!scannerClassName.equals(stackTrace[i].getClassName())) {
369 break;
370 }
371 }
372 for (; i < stackTrace.length-1; i++) {
373 if (!matches(stackTrace[i].getMethodName(), excludedMethods)
374 && !matches(stackTrace[i].getClassName(), excludedClasses)) {
375 break;
376 }
377 }
378 //Arrays.copyOf(stackTrace, i, stackTrace.length);
379 return (StackTraceElement[]) ArrayUtils.subarray(stackTrace, i, stackTrace.length);
380 }
381
382 }
383