001 /*
002 * $Id: JoinPointHelper.java,v 1.19 2014/02/07 18:29:58 oboehm Exp $
003 *
004 * Copyright (c) 2008 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 23.10.2008 by oliver (ob@oasd.de)
019 */
020 package patterntesting.runtime.util;
021
022 import java.lang.annotation.Annotation;
023 import java.lang.reflect.*;
024 import java.util.regex.Pattern;
025
026 import org.aspectj.lang.*;
027 import org.aspectj.lang.reflect.*;
028
029 import patterntesting.annotation.check.runtime.*;
030 import patterntesting.runtime.annotation.DontLogMe;
031
032 /**
033 * The Class JoinPointHelper.
034 *
035 * @author <a href="boehm@javatux.de">oliver</a>
036 * @since 23.10.2008
037 * @version $Revision: 1.19 $
038 */
039 public final class JoinPointHelper {
040
041 private static Pattern[] ADVICE_PATTERNS = {
042 Pattern.compile("ajc\\$.*"),
043 Pattern.compile(".*\\$_aroundBody.*")
044 };
045
046 /** Utility class - no need to instantiate it */
047 private JoinPointHelper() {}
048
049 /**
050 * Gets the given joinpoint as string.
051 *
052 * @param joinpoint the joinpoint
053 *
054 * @return the as string
055 */
056 public static String getAsString(final JoinPoint joinpoint) {
057 Signature sig = joinpoint.getSignature();
058 Object[] args = joinpoint.getArgs();
059 if (sig instanceof FieldSignature) {
060 if (args.length == 0) {
061 return "get " + sig.toShortString();
062 } else {
063 return "set " + sig.toShortString() + " = "
064 + getArgAsString(args[0]);
065 }
066 } else if (sig instanceof CodeSignature) {
067 Annotation[][] paramAnnotations = getParameterAnnotations(joinpoint);
068 return SignatureHelper.getAsString(sig.getDeclaringTypeName(), sig)
069 + getArgsAsString(args, paramAnnotations);
070 } else {
071 return SignatureHelper.getAsString(sig.getDeclaringTypeName(), sig)
072 + getArgsAsString(args);
073 }
074 }
075
076 /**
077 * Gets the as short string.
078 *
079 * @param joinpoint the joinpoint
080 *
081 * @return the as short string
082 */
083 public static String getAsShortString(final JoinPoint joinpoint) {
084 Signature sig = joinpoint.getSignature();
085 if (sig instanceof FieldSignature) {
086 return getAsString(joinpoint);
087 }
088 Object[] args = joinpoint.getArgs();
089 if (sig instanceof CatchClauseSignature) {
090 return SignatureHelper.getAsString(sig.getDeclaringType().getSimpleName(), sig) + "("
091 + args[0].getClass().getName() + ")";
092 }
093 return SignatureHelper.getAsString(sig.getDeclaringType().getSimpleName(), sig)
094 + getArgsAsShortString(args);
095 }
096
097 /**
098 * Gets the as long string.
099 *
100 * @param joinpoint the joinpoint
101 *
102 * @return the as short string
103 */
104 public static String getAsLongString(final JoinPoint joinpoint) {
105 Signature sig = joinpoint.getSignature();
106 if (sig instanceof FieldSignature) {
107 return getAsString(joinpoint);
108 }
109 Object[] args = joinpoint.getArgs();
110 return SignatureHelper.getAsString(sig.getDeclaringType().getName(), sig)
111 + getArgsAsString(args);
112 }
113
114 /**
115 * Gets the parameter annotations.
116 *
117 * @param joinpoint should be a Method- or Constructor-Signature
118 *
119 * @return the annotations of a method or constructor, otherwise null
120 */
121 @MayReturnNull
122 public static Annotation[][] getParameterAnnotations(final JoinPoint joinpoint) {
123 Signature sig = joinpoint.getSignature();
124 if (sig instanceof MethodSignature) {
125 Method method = ((MethodSignature) sig).getMethod();
126 if (method == null) {
127 return null;
128 }
129 return method.getParameterAnnotations();
130 } else if (sig instanceof ConstructorSignature) {
131 ConstructorSignature ctorSig = (ConstructorSignature) sig;
132 return getParameterAnnotations(ctorSig);
133 } else {
134 return null;
135 }
136 }
137
138 /**
139 * Normally you get an annotation array with the size of the constructor
140 * parameters. But not for inner classes. Here the first parameter of the
141 * constructor is the embedded class. In this case we must correct the
142 * annotation array
143 *
144 * @param ctorSig
145 * @return
146 */
147 @MayReturnNull
148 private static Annotation[][] getParameterAnnotations(final ConstructorSignature ctorSig) {
149 Constructor<?> ctor = ctorSig.getConstructor();
150 Annotation[][] annotations = ctor.getParameterAnnotations();
151 if (isInnerClass(ctorSig)) {
152 Annotation[][] corrected = new Annotation[annotations.length+1][0];
153 corrected[0] = null;
154 System.arraycopy(annotations, 0, corrected, 1, annotations.length);
155 return corrected;
156 }
157 return annotations;
158 }
159
160 /**
161 * Returns the annotation for the given JoinPoint.
162 *
163 * @since 1.0
164 * @param jp the JoinPoint
165 * @param annotationClass the wanted annotation
166 * @return the annotation (if found) or null
167 */
168 @MayReturnNull
169 public static Annotation getClassAnnotation(final JoinPoint jp,
170 final Class<? extends Annotation> annotationClass) {
171 Class<?> thisClass = jp.getSignature().getDeclaringType();
172 assert thisClass != null;
173 return thisClass.getAnnotation(annotationClass);
174 }
175
176 /**
177 * Gets the args as string.
178 *
179 * @param args the args
180 *
181 * @return the args as string
182 */
183 @NullArgsAllowed
184 public static String getArgsAsString(final Object[] args) {
185 if ((args == null) || (args.length == 0)) {
186 return "()";
187 }
188 StringBuilder sb = new StringBuilder("(");
189 sb.append(getArgAsString(args[0]));
190 for (int i = 1; i < args.length; i++) {
191 sb.append(", ");
192 sb.append(getArgAsString(args[i]));
193 }
194 sb.append(")");
195 return sb.toString();
196 }
197
198 @NullArgsAllowed
199 private static String getArgsAsString(final Object[] args,
200 final Annotation[][] annotations) {
201 if ((args == null) || (args.length == 0)) {
202 return "()";
203 }
204 StringBuilder sb = new StringBuilder("(");
205 sb.append(getArgAsString(args[0], annotations[0]));
206 for (int i = 1; i < args.length; i++) {
207 sb.append(", ");
208 sb.append(getArgAsString(args[i], annotations[i]));
209 }
210 sb.append(")");
211 return sb.toString();
212 }
213
214 /**
215 * Gets the args as short string. If the resulting argument string will
216 * be too long it will be abbreviated.
217 *
218 * @param args the args
219 * @return the args as short string
220 * @since 1.4.1
221 */
222 @NullArgsAllowed
223 public static String getArgsAsShortString(final Object[] args) {
224 String shortString = getArgsAsString(args);
225 if (shortString.length() < 20) {
226 return shortString;
227 }
228 StringBuilder sb = new StringBuilder("(");
229 sb.append(getArgAsShortString(args[0]));
230 int n = args.length;
231 if (n > 3) {
232 n = 3;
233 }
234 for (int i = 1; i < n; i++) {
235 sb.append(", ");
236 sb.append(getArgAsShortString(args[i]));
237 }
238 if (args.length > 3) {
239 sb.append(", ..");
240 }
241 sb.append(")");
242 return sb.toString();
243 }
244
245 /**
246 * Gets the arg as string.
247 *
248 * @param obj the obj
249 *
250 * @return the arg as string
251 */
252 @NullArgsAllowed
253 public static String getArgAsString(final Object obj) {
254 if (obj == null) {
255 return "(null)";
256 }
257 if (obj instanceof String) {
258 return "\"" + obj + "\"";
259 }
260 return obj.toString();
261 }
262
263 /**
264 * Gets the arg as short string.
265 *
266 * @param obj the obj
267 * @return the arg as short string
268 * @since 1.4.1
269 */
270 @NullArgsAllowed
271 public static String getArgAsShortString(final Object obj) {
272 if (obj == null) {
273 return "(null)";
274 }
275 String shortString = Converter.toShortString(obj);
276 if (obj instanceof String) {
277 return "\"" + shortString + "\"";
278 }
279 return shortString;
280 }
281
282 @NullArgsAllowed
283 private static String getArgAsString(final Object obj,
284 final Annotation[] annotations) {
285 if (hasDontLogMeAnnotation(annotations)) {
286 return "*";
287 } else {
288 return getArgAsString(obj);
289 }
290 }
291
292 private static boolean hasDontLogMeAnnotation(final Annotation[] annotations) {
293 if (annotations == null) {
294 return false;
295 }
296 for (Annotation annotation : annotations) {
297 if (annotation instanceof DontLogMe) {
298 return true;
299 }
300 }
301 return false;
302 }
303
304 /**
305 * The Java compiler generates something like OuterClass$InnerClass
306 * as name for the inner class. So if a name contains a '$' it is
307 * probably an inner class.
308 *
309 * @param jp the jp
310 *
311 * @return true if joinpoint belongs to an inner class
312 */
313 public static boolean isInnerClass(final JoinPoint jp) {
314 return isInnerClass(jp.getSignature());
315 }
316
317 /**
318 * The Java compiler generates something like OuterClass$InnerClass
319 * as name for the inner class. So if a name contains a '$' it is
320 * probably an inner class.
321 *
322 * @param sig the sig
323 *
324 * @return true if signature belongs to an inner class
325 */
326 public static boolean isInnerClass(final Signature sig) {
327 String typeName = sig.getDeclaringTypeName();
328 return typeName.contains("$");
329 }
330
331 /**
332 * Gets the caller class by examing the stacktrace.
333 * <p>
334 * Sometime the caller of the method is an aspect. Because normally you
335 * do not want the aspect it is filtered out. If you really want it call
336 * {@link #getCallerOf(JoinPoint, Pattern...)} direct with an empty pattern
337 * argument.
338 * </p>
339 *
340 * @return the caller of a joinpoint
341 * @see #getCallerOf(JoinPoint, Pattern...)
342 */
343 public static Class<?> getCallerClass() {
344 return StackTraceScanner.getCallerClass(ADVICE_PATTERNS, JoinPointHelper.class);
345 }
346
347 /**
348 * Gets the caller class by examing the stacktrace.
349 * <p>
350 * Sometime the caller of the method is an aspect. With this method you
351 * have the chance to list the aspects which should be ignored.
352 * </p>
353 *
354 * @param excluded the excluded
355 * @return the caller of a joinpoint
356 */
357 public static Class<?> getCallerClass(final Class<?>... excluded) {
358 return StackTraceScanner.getCallerClass(ADVICE_PATTERNS, excluded);
359 }
360
361 /**
362 * Gets the caller class by examing the stacktrace.
363 *
364 * @param excluded a list of filters which should be not considered as
365 * caller
366 * @return the caller of
367 * @deprecated Use {@link StackTraceScanner#getCallerClass(Pattern...)} instead
368 */
369 @Deprecated
370 public static Class<?> getCallerClass(final Pattern... excluded) {
371 return StackTraceScanner.getCallerClass(excluded);
372 }
373
374 /**
375 * Gets the caller of the given joinpoint. For a call joinpoint you have
376 * "this" as attribute which gives you the caller. But for an execution
377 * joinpoint "this" is not the caller but the object of execution. If you
378 * want the caller of the excution joinpoint call this method here.
379 * <p>
380 * Sometime the caller of the method is an aspect. Because normally you
381 * do not want the aspect it is filtered out. If you really want it call
382 * {@link #getCallerOf(JoinPoint, Pattern...)} direct with an empty pattern
383 * argument.
384 * </p>
385 *
386 * @param jp the (execution) joinpoint
387 * @return the caller of the given joinpoint
388 * @see #getCallerOf(JoinPoint, Pattern...)
389 */
390 public static StackTraceElement getCallerOf(final JoinPoint jp) {
391 return getCallerOf(jp, ADVICE_PATTERNS);
392 }
393
394 /**
395 * Gets the caller of the given joinpoint. For a call joinpoint you have
396 * "this" as attribute which gives you the caller. But for an execution
397 * joinpoint "this" is not the caller but the object of execution. If you
398 * want the caller of the excution joinpoint call this method here.
399 *
400 * @param jp the (execution) joinpoint
401 * @param excluded a list of filters which should be not considered as
402 * caller
403 * @return the caller of
404 */
405 public static StackTraceElement getCallerOf(final JoinPoint jp, final Pattern... excluded) {
406 return StackTraceScanner.getCallerOf(jp.getSignature(), excluded);
407 }
408
409 }