/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
package org.codehaus.groovy.reflection; | |
import java.util.Collection; | |
import java.util.HashSet; | |
import java.util.Set; | |
import java.util.Collections; | |
/** | |
* This class contains utility methods to determine which class called the | |
* current class to multiple levels of depth. Calls used to handle the | |
* groovy MOP are excluded from the level counting. | |
*/ | |
public class ReflectionUtils { | |
// these are packages in the call stack that are only part of the groovy MOP | |
private static final Set<String> IGNORED_PACKAGES = new HashSet<String>(); | |
static { | |
//IGNORED_PACKAGES.add("java.lang.reflect"); | |
IGNORED_PACKAGES.add("groovy.lang"); | |
IGNORED_PACKAGES.add("org.codehaus.groovy.reflection"); | |
IGNORED_PACKAGES.add("org.codehaus.groovy.runtime.callsite"); | |
IGNORED_PACKAGES.add("org.codehaus.groovy.runtime.metaclass"); | |
IGNORED_PACKAGES.add("org.codehaus.groovy.runtime"); | |
IGNORED_PACKAGES.add("sun.reflect"); | |
IGNORED_PACKAGES.add("java.lang.invoke"); | |
IGNORED_PACKAGES.add("org.codehaus.groovy.vmplugin.v7"); | |
} | |
private static final ClassContextHelper HELPER = new ClassContextHelper(); | |
/** | |
* Determine whether or not the getCallingClass methods will return | |
* any sensible results. On JVMs that are not Sun derived i.e. | |
* (gcj, Harmony) this will likely return false. When not available | |
* all getCallingClass methods will return null. | |
* | |
* @return true if getCallingClass can return anything but null, false if | |
* it will only return null. | |
*/ | |
public static boolean isCallingClassReflectionAvailable() { | |
return true; | |
} | |
/** | |
* Get the immediate calling class, ignoring MOP frames. | |
* | |
* @return The Class of the caller | |
*/ | |
public static Class getCallingClass() { | |
return getCallingClass(1); | |
} | |
/** | |
* Get the called that is matchLevel stack frames before the call, | |
* ignoring MOP frames. | |
* | |
* @param matchLevel how may call stacks down to look. | |
* If it is less than 1 it is treated as though it was 1. | |
* @return The Class of the matched caller, or null if there aren't | |
* enough stackframes to satisfy matchLevel | |
*/ | |
public static Class getCallingClass(int matchLevel) { | |
return getCallingClass(matchLevel, Collections.EMPTY_SET); | |
} | |
/** | |
* Get the called that is matchLevel stack frames before the call, | |
* ignoring MOP frames and desired exclude packages. | |
* | |
* @param matchLevel how may call stacks down to look. | |
* If it is less than 1 it is treated as though it was 1. | |
* @param extraIgnoredPackages A collection of string names of packages to exclude | |
* in addition to the MOP packages when counting stack frames. | |
* @return The Class of the matched caller, or null if there aren't | |
* enough stackframes to satisfy matchLevel | |
*/ | |
public static Class getCallingClass(int matchLevel, Collection<String> extraIgnoredPackages) { | |
Class[] classContext = HELPER.getClassContext(); | |
int depth = 0; | |
try { | |
Class c; | |
// this super class stuff is for Java 1.4 support only | |
// it isn't needed on a 5.0 VM | |
Class sc; | |
do { | |
do { | |
c = classContext[depth++]; | |
if (c != null) { | |
sc = c.getSuperclass(); | |
} else { | |
sc = null; | |
} | |
} while (classShouldBeIgnored(c, extraIgnoredPackages) | |
|| superClassShouldBeIgnored(sc)); | |
} while (c != null && matchLevel-- > 0 && depth<classContext.length); | |
return c; | |
} catch (Throwable t) { | |
return null; | |
} | |
} | |
private static boolean superClassShouldBeIgnored(Class sc) { | |
return ((sc != null) && (sc.getPackage() != null) && "org.codehaus.groovy.runtime.callsite".equals(sc.getPackage().getName())); | |
} | |
private static boolean classShouldBeIgnored(Class c, Collection<String> extraIgnoredPackages) { | |
return ((c != null) | |
&& (c.isSynthetic() | |
|| (c.getPackage() != null | |
&& (IGNORED_PACKAGES.contains(c.getPackage().getName()) | |
|| extraIgnoredPackages.contains(c.getPackage().getName()))))); | |
} | |
private static class ClassContextHelper extends SecurityManager { | |
@Override | |
public Class[] getClassContext() { | |
return super.getClassContext(); | |
} | |
} | |
} |