/*
* Copyright 2012 Chris Pheby
*
* 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.jadira.scanner.classpath.types;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.annotation.Annotation;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.jadira.scanner.classpath.ClasspathResolver;
import org.jadira.scanner.core.exception.ClasspathAccessException;
import org.jadira.scanner.core.helper.JavassistMethodInfoHelper;
public abstract class JOperation extends JElement {
private MethodInfo methodInfo;
private final JType enclosingType;
protected JOperation(MethodInfo methodInfo, JType enclosingType, ClasspathResolver resolver) {
super(methodInfo == null ? null : methodInfo.getName(), resolver);
this.methodInfo = methodInfo;
this.enclosingType = enclosingType;
}
public JType getEnclosingType() {
return enclosingType;
}
@Override
public Set<JAnnotation<?>> getAnnotations() {
AnnotationsAttribute visible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
AnnotationsAttribute invisible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.invisibleTag);
Set<JAnnotation<?>> annotations = new HashSet<JAnnotation<?>>();
List<Annotation> annotationsList = new ArrayList<Annotation>();
if (visible != null) {
annotationsList.addAll(Arrays.asList(visible.getAnnotations()));
}
if (invisible != null) {
annotationsList.addAll(Arrays.asList(invisible.getAnnotations()));
}
for (Annotation nextAnnotation : annotationsList) {
annotations.add(JAnnotation.getJAnnotation(nextAnnotation, this, getResolver()));
}
return annotations;
}
@Override
public <A extends java.lang.annotation.Annotation> JAnnotation<A> getAnnotation(Class<A> annotation) {
AnnotationsAttribute visible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
AnnotationsAttribute invisible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.invisibleTag);
List<javassist.bytecode.annotation.Annotation> annotationsList = new ArrayList<Annotation>();
if (visible != null) {
annotationsList.addAll(Arrays.asList(visible.getAnnotations()));
}
if (invisible != null) {
annotationsList.addAll(Arrays.asList(invisible.getAnnotations()));
}
for (javassist.bytecode.annotation.Annotation nextAnnotation : annotationsList) {
if (annotation.getName().equals(nextAnnotation.getTypeName())) {
@SuppressWarnings("unchecked") JAnnotation<A> retVal = (JAnnotation<A>) JAnnotation.getJAnnotation(nextAnnotation, this, getResolver());
return retVal;
}
}
return null;
}
protected MethodInfo getMethodInfo() {
return methodInfo;
}
public List<JParameter> getParameters() throws ClasspathAccessException {
List<JParameter> params = new ArrayList<JParameter>();
String[] paramTypes = JavassistMethodInfoHelper.getMethodParamTypeNames(methodInfo);
for (int i = 0; i < paramTypes.length; i++) {
params.add(JParameter.getJParameter(i, this, getResolver()));
}
return params;
}
public Method getActualMethod() throws ClasspathAccessException {
try {
return getEnclosingType().getActualClass().getMethod(getName(), getMethodParamClasses(methodInfo));
} catch (SecurityException e) {
throw new ClasspathAccessException("Problem obtaining method: " + e.getMessage(), e);
} catch (NoSuchMethodException e) {
throw new ClasspathAccessException("Problem finding method: " + e.getMessage(), e);
}
}
// public List<JLocalVariable> getEnclosedLocalVariables()
// public List<JAnonymousClass> getEnclosedAnonymousClasses()
@Override
public JType getEnclosingElement() {
return enclosingType;
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
builder.append("name",this.getName());
builder.append("enclosingType",this.getEnclosingType());
builder.append("parameters",this.getParameters());
return builder.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
JOperation rhs = (JOperation) obj;
return new EqualsBuilder()
.appendSuper(super.equals(obj))
.append(enclosingType, rhs.enclosingType).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(11, 47).append(super.hashCode())
.append(enclosingType).toHashCode();
}
protected Class<?>[] getMethodParamClasses(MethodInfo methodInfo) throws ClasspathAccessException {
String[] classNames = JavassistMethodInfoHelper.getMethodParamTypeNames(methodInfo);
Class<?>[] retArray = new Class<?>[classNames.length];
for (int i = 0; i < classNames.length; i++) {
if (!"".equals(classNames[i])) {
retArray[i] = decodeFieldType(classNames[i]);
}
}
return retArray;
}
private Class<?> decodeFieldType(String componentType) {
char type = componentType.charAt(0);
String fieldContent = componentType.substring(1);
switch (type) {
// L<classname>; reference an instance of class <classname>
case 'L':
return getResolver().loadClass(fieldContent.replace('/', '.'));
// B byte signed byte
case 'B':
return Byte.class;
// C char Unicode character
case 'C':
return Character.class;
// D double double-precision floating-point value
case 'D':
return Double.class;
// F float single-precision floating-point value
case 'F':
return Float.class;
// I int integer
case 'I':
return Integer.class;
// J long long integer
case 'J':
return Long.class;
// S short signed short
case 'S':
return Short.class;
// Z boolean true or false
case 'Z':
return Boolean.class;
// [ reference one array dimension
case '[':
return Arrays.class;
}
return null;
}
}