View Javadoc
1   /*
2    *  Copyright 2012 Chris Pheby
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.jadira.scanner.classpath.types;
17  
18  import java.lang.reflect.Method;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Set;
24  
25  import javassist.bytecode.AnnotationsAttribute;
26  import javassist.bytecode.MethodInfo;
27  import javassist.bytecode.annotation.Annotation;
28  
29  import org.apache.commons.lang3.builder.EqualsBuilder;
30  import org.apache.commons.lang3.builder.HashCodeBuilder;
31  import org.apache.commons.lang3.builder.ToStringBuilder;
32  import org.apache.commons.lang3.builder.ToStringStyle;
33  import org.jadira.scanner.classpath.ClasspathResolver;
34  import org.jadira.scanner.core.exception.ClasspathAccessException;
35  import org.jadira.scanner.core.helper.JavassistMethodInfoHelper;
36  
37  public abstract class JOperation extends JElement {
38  
39      private MethodInfo methodInfo;
40      private final JType enclosingType;
41  
42      protected JOperation(MethodInfo methodInfo, JType enclosingType, ClasspathResolver resolver) {
43          super(methodInfo == null ? null : methodInfo.getName(), resolver);
44          this.methodInfo = methodInfo;
45          this.enclosingType = enclosingType;
46      }
47  
48      public JType getEnclosingType() {
49          return enclosingType;
50      }
51      
52      @Override
53      public Set<JAnnotation<?>> getAnnotations() {
54  
55          AnnotationsAttribute visible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
56          AnnotationsAttribute invisible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.invisibleTag);
57  
58          Set<JAnnotation<?>> annotations = new HashSet<JAnnotation<?>>();
59  
60          List<Annotation> annotationsList = new ArrayList<Annotation>();
61          if (visible != null) {
62              annotationsList.addAll(Arrays.asList(visible.getAnnotations()));
63          }
64          if (invisible != null) {
65              annotationsList.addAll(Arrays.asList(invisible.getAnnotations()));
66          }
67  
68          for (Annotation nextAnnotation : annotationsList) {
69              annotations.add(JAnnotation.getJAnnotation(nextAnnotation, this, getResolver()));
70          }
71  
72          return annotations;
73      }
74  
75      @Override
76      public <A extends java.lang.annotation.Annotation> JAnnotation<A> getAnnotation(Class<A> annotation) {
77  
78          AnnotationsAttribute visible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
79          AnnotationsAttribute invisible = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.invisibleTag);
80  
81          List<javassist.bytecode.annotation.Annotation> annotationsList = new ArrayList<Annotation>();
82          if (visible != null) {
83              annotationsList.addAll(Arrays.asList(visible.getAnnotations()));
84          }
85          if (invisible != null) {
86              annotationsList.addAll(Arrays.asList(invisible.getAnnotations()));
87          }
88  
89          for (javassist.bytecode.annotation.Annotation nextAnnotation : annotationsList) {
90              if (annotation.getName().equals(nextAnnotation.getTypeName())) {
91                  @SuppressWarnings("unchecked") JAnnotation<A> retVal = (JAnnotation<A>) JAnnotation.getJAnnotation(nextAnnotation, this, getResolver());
92                  return retVal;
93              }
94          }
95  
96          return null;
97      }
98  
99      protected MethodInfo getMethodInfo() {
100         return methodInfo;
101     }
102 
103     public List<JParameter> getParameters() throws ClasspathAccessException {
104 
105         List<JParameter> params = new ArrayList<JParameter>();
106         String[] paramTypes = JavassistMethodInfoHelper.getMethodParamTypeNames(methodInfo);
107         for (int i = 0; i < paramTypes.length; i++) {
108             params.add(JParameter.getJParameter(i, this, getResolver()));
109         }
110 	    return params;
111     }
112 
113     public Method getActualMethod() throws ClasspathAccessException {
114 
115         try {
116             return getEnclosingType().getActualClass().getMethod(getName(), getMethodParamClasses(methodInfo));
117         } catch (SecurityException e) {
118             throw new ClasspathAccessException("Problem obtaining method: " + e.getMessage(), e);
119         } catch (NoSuchMethodException e) {
120             throw new ClasspathAccessException("Problem finding method: " + e.getMessage(), e);
121         }
122     }
123 
124     // public List<JLocalVariable> getEnclosedLocalVariables()
125 
126     // public List<JAnonymousClass> getEnclosedAnonymousClasses()
127 
128     @Override
129     public JType getEnclosingElement() {
130         return enclosingType;
131     }
132     
133     @Override
134     public String toString() {
135     	
136     	ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
137     	builder.append("name",this.getName());
138     	builder.append("enclosingType",this.getEnclosingType());
139     	builder.append("parameters",this.getParameters());
140     	
141     	return builder.toString();
142     }
143     
144     @Override
145 	public boolean equals(Object obj) {
146 		if (obj == null) {
147 			return false;
148 		}
149 		if (obj == this) {
150 			return true;
151 		}
152 		if (obj.getClass() != getClass()) {
153 			return false;
154 		}
155 		JOperation rhs = (JOperation) obj;
156 		return new EqualsBuilder()
157 			 	.appendSuper(super.equals(obj))
158 				.append(enclosingType, rhs.enclosingType).isEquals();
159 	}
160 
161     @Override
162 	public int hashCode() {
163 		return new HashCodeBuilder(11, 47).append(super.hashCode())
164 				.append(enclosingType).toHashCode();
165 	}
166     
167 
168     protected Class<?>[] getMethodParamClasses(MethodInfo methodInfo) throws ClasspathAccessException {
169 
170         String[] classNames = JavassistMethodInfoHelper.getMethodParamTypeNames(methodInfo);
171         Class<?>[] retArray = new Class<?>[classNames.length];
172 
173         for (int i = 0; i < classNames.length; i++) {
174             if (!"".equals(classNames[i])) {
175                 retArray[i] = decodeFieldType(classNames[i]);
176             }
177         }
178         return retArray;
179     }
180 
181     private Class<?> decodeFieldType(String componentType) {
182 
183         char type = componentType.charAt(0);
184         String fieldContent = componentType.substring(1);
185 
186         switch (type) {
187         // L<classname>; reference an instance of class <classname>
188         case 'L': 
189             return getResolver().loadClass(fieldContent.replace('/', '.'));
190         // B byte signed byte
191         case 'B': 
192             return Byte.class;
193         // C char Unicode character
194         case 'C': 
195             return Character.class;
196         // D double double-precision floating-point value
197         case 'D': 
198             return Double.class;
199         // F float single-precision floating-point value        
200         case 'F': 
201             return Float.class;
202         // I int integer
203         case 'I': 
204             return Integer.class;
205         // J long long integer
206         case 'J': 
207             return Long.class;
208         // S short signed short
209         case 'S': 
210             return Short.class;
211         // Z boolean true or false
212         case 'Z': 
213             return Boolean.class;
214         // [ reference one array dimension
215         case '[': 
216             return Arrays.class;
217         }
218         return null;
219     }
220 }