/**
* Copyright 2014 SAP AG
*
* Licensed 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.aim.mainagent.scope;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.aim.api.exceptions.InstrumentationException;
import org.aim.api.instrumentation.AbstractInstAPIScope;
import org.aim.api.instrumentation.AbstractScopeAnalyzer;
import org.aim.api.instrumentation.description.internal.FlatScopeEntity;
import org.aim.description.restrictions.Restriction;
import org.aim.mainagent.utils.MethodSignature;
import org.aim.mainagent.utils.Utils;
/**
* Analyzes a certain API scope.
*
* @author Alexander Wert
*
*/
public class APIScopeAnalyzer extends AbstractScopeAnalyzer {
private Map<Class<?>, List<MethodSignature>> methodsToMatch;
private Set<Class<Annotation>> methodAnnotationsToMatch;
private Restriction restriction;
/**
* Constructor.
*
* @param apiScope
* concrete instance of an AbstractInstAPIScope specifying a
* concrete scope.
* @param allLoadedClasses
* all classes loaded by the JVM
* @throws InstrumentationException
* if an API class or interface could not be found.
*/
@SuppressWarnings("rawtypes")
public APIScopeAnalyzer(AbstractInstAPIScope apiScope, List<Class> allLoadedClasses)
throws InstrumentationException {
methodsToMatch = new HashMap<>();
for (String containerName : apiScope.getMethodsToMatch().keySet()) {
try {
Class<?> containerClass = Class.forName(containerName);
List<MethodSignature> signatures = new ArrayList<>();
for (String apiMethod : apiScope.getMethodsToMatch().get(containerName)) {
String methodName = apiMethod.substring(0, apiMethod.indexOf('('));
Class<?>[] paramTypes = Utils.getParameterTypes(apiMethod);
signatures.add(new MethodSignature(methodName, paramTypes));
}
methodsToMatch.put(containerClass, signatures);
} catch (ClassNotFoundException e) {
throw new InstrumentationException("Failed determining scope " + apiScope.getClass().getName(), e);
}
}
methodAnnotationsToMatch = new HashSet<>();
for (String annotationName : apiScope.getMethodAnnotationsToMatch()) {
try {
Class<Annotation> annotationClass = findAnnotation(allLoadedClasses, annotationName);
if (annotationClass != null) {
methodAnnotationsToMatch.add(annotationClass);
}
} catch (ClassNotFoundException e) {
throw new InstrumentationException("Failed determining scope " + apiScope.getClass().getName(), e);
}
}
}
@SuppressWarnings("rawtypes")
private Class<Annotation> findAnnotation(List<Class> allLoadedClasses, String annotationName)
throws ClassNotFoundException {
for (Class<?> clazz : allLoadedClasses) {
try {
if (clazz.getName().equals(annotationName)) {
@SuppressWarnings("unchecked")
Class<Annotation> annotationClass = (Class<Annotation>) clazz;
return annotationClass;
}
} catch (Throwable t) {
continue;
}
}
return null;
}
@Override
public void visitClass(Class<?> clazz, Set<FlatScopeEntity> scopeEntities) {
if (clazz == null || !Utils.isNormalClass(clazz)) {
return;
}
if (restriction.hasModifierRestrictions() && restriction.modifierSetExcluded(Modifier.PUBLIC)) {
return;
}
if (restriction.isExcluded(clazz.getName())) {
return;
}
if (scopeEntities == null) {
scopeEntities = new HashSet<>();
}
Set<Method> methods = new HashSet<>();
for (Method m : clazz.getMethods()) {
if (!Modifier.isAbstract(m.getModifiers()) && !Modifier.isNative(m.getModifiers())
&& m.getDeclaringClass().equals(clazz)) {
methods.add(m);
}
}
for (Method m : clazz.getDeclaredMethods()) {
if (!Modifier.isAbstract(m.getModifiers()) && !Modifier.isNative(m.getModifiers())) {
methods.add(m);
}
}
for (Method method : methods) {
for (Class<?> apiClass : methodsToMatch.keySet()) {
if (apiClass.isAssignableFrom(clazz)) {
for (MethodSignature apiMethodSignature : methodsToMatch.get(apiClass)) {
if (method.getName().equals(apiMethodSignature.getMethodName())
&& Arrays.equals(method.getParameterTypes(), apiMethodSignature.getParameters())) {
scopeEntities.add(new FlatScopeEntity(clazz, Utils.getMethodSignature(method, true)));
}
}
}
}
for (Class<Annotation> annotationClass : methodAnnotationsToMatch) {
if (method.getAnnotation(annotationClass) != null) {
scopeEntities.add(new FlatScopeEntity(clazz, Utils.getMethodSignature(method, true)));
break;
}
}
}
}
@Override
public void setRestriction(Restriction restriction) {
this.restriction = restriction;
}
}