001 /*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 * http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014 package org.apache.commons.classscan.bcel;
015
016 import java.io.IOException;
017 import java.util.Set;
018
019 import org.apache.bcel.classfile.ClassFormatException;
020 import org.apache.bcel.classfile.ClassParser;
021 import org.apache.bcel.classfile.Field;
022 import org.apache.bcel.classfile.JavaClass;
023 import org.apache.bcel.classfile.Method;
024 import org.apache.commons.classscan.MetaClassPathElement;
025 import org.apache.commons.classscan.model.MetaAnnotation;
026 import org.apache.commons.classscan.model.MetaClass;
027 import org.apache.commons.classscan.model.MetaField;
028 import org.apache.commons.classscan.model.MetaMethod;
029 import org.apache.commons.classscan.spi.model.SpiMetaClass;
030 import org.apache.commons.classscan.spi.model.SpiMetaClassLoader;
031 import org.apache.commons.classscan.spi.model.SpiMetaField;
032 import org.apache.commons.classscan.spi.model.SpiMetaMethod;
033 import org.apache.commons.classscan.util.NameSet;
034 import org.apache.commons.classscan.util.ReadOnlySet;
035 import org.apache.commons.classscan.util.ResolveSet;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038
039 public class BcelClass implements SpiMetaClass {
040
041 private static final Logger logger = LoggerFactory.getLogger(BcelClass.class);
042
043 private final MetaClassPathElement location;
044 private final String canonicalName;
045 private String parentName;
046 private MetaClass parent;
047 private String[] interfaceNames;
048 private Set<MetaClass> interfaces;
049 private final AnnotationMap annotations;
050 private final ResolveSet<SpiMetaMethod> methods;
051 private final ResolveSet<SpiMetaField> fields;
052 private Boolean resolutionResult;
053
054 public BcelClass(MetaClassPathElement location, ClassParser parser) throws ClassFormatException, IOException {
055 this.location = location;
056 JavaClass bcelClass = parser.parse();
057 canonicalName = bcelClass.getClassName();
058 parentName = getParentName(bcelClass);
059 interfaceNames = bcelClass.getInterfaceNames();
060 annotations = AnnotationMap.createAnnotations(bcelClass.getAnnotationEntries());
061 methods = createMethods(bcelClass);
062 fields = createFields(bcelClass);
063 }
064
065 @Override
066 public boolean resolve(SpiMetaClassLoader classLoader) {
067 if(resolutionResult == null) {
068 // set result to true to prevent re-resolution of classes involved in cyclic graphs
069 resolutionResult = Boolean.TRUE;
070 resolutionResult= Boolean.valueOf(resolveParent(classLoader)
071 && resolveInterfaces(classLoader)
072 && annotations.resolve(classLoader)
073 && methods.resolve(classLoader)
074 && fields.resolve(classLoader));
075 }
076 return resolutionResult;
077 }
078
079 private boolean resolveParent(SpiMetaClassLoader classLoader) {
080 boolean success = true;
081 if(parentName == null) {
082 // this is java.lang.Object
083 // parent should remain null
084 }
085 else {
086 parent = classLoader.resolveMetaClass(parentName);
087 if(parent == null) {
088 logger.info("Cannot find parent "+parentName+" for class "+canonicalName);
089 success= false;
090 }
091 parentName = null;
092 }
093 return success;
094 }
095
096 private static final ReadOnlySet<MetaClass> EMPTY_INTERFACES = new ReadOnlySet<MetaClass>(new MetaClass[0]);
097
098 private boolean resolveInterfaces(final SpiMetaClassLoader classLoader) {
099 boolean success= true;
100 if (interfaceNames==null || interfaceNames.length == 0) {
101 interfaces= EMPTY_INTERFACES;
102 }
103 else {
104 MetaClass[] metaClasses = new MetaClass[interfaceNames.length];
105 int i= 0;
106 for(String interfaceName : interfaceNames) {
107 MetaClass mc = classLoader.resolveMetaClass(interfaceName);
108 if(mc == null) {
109 logger.info("Cannot find interface "+interfaceName+" for class "+canonicalName);
110 success= false;
111 }
112 metaClasses[i++] = mc;
113 }
114 interfaces= new ReadOnlySet<MetaClass>(metaClasses);
115 }
116 interfaceNames= null;
117 return success;
118 }
119
120 @Override
121 public MetaClassPathElement getClassLocation() {
122 return location;
123 }
124
125 @Override
126 public String getName() {
127 return canonicalName;
128 }
129
130 @Override
131 public MetaClass getParent() {
132 return parent;
133 }
134
135 private String getParentName(JavaClass bcelClass) {
136 String superclassName = bcelClass.getSuperclassName();
137 if (superclassName.equals(canonicalName)) { // bcel behaves
138 // unexpectedly, Object's
139 // super class is Object
140 return null;
141 }
142 return superclassName;
143 }
144
145 @Override
146 public Set<MetaClass> getInterfaces() {
147 return interfaces;
148 }
149
150 @Override
151 public Set<? extends MetaAnnotation> getAnnotations() {
152 return annotations;
153 }
154
155 @Override
156 public MetaAnnotation getAnnotation(String annotationName) {
157 return annotations.getValue(annotationName);
158 }
159
160 @Override
161 public Set<? extends MetaMethod> getMethods() {
162 return methods;
163 }
164
165 private NameSet<SpiMetaMethod> createMethods(JavaClass bcelClass) {
166 Method[] methods = bcelClass.getMethods();
167 if (methods.length == 0) {
168 return NameSet.emptyNameSet();
169 }
170
171 SpiMetaMethod[] metaMethods = new SpiMetaMethod[methods.length];
172 for(int i = 0; i<methods.length; ++i) {
173 metaMethods[i]= new BcelMethod(methods[i]);
174 }
175
176 return new NameSet<SpiMetaMethod>(metaMethods);
177 }
178
179 @Override
180 public Set<? extends MetaField> getFields() {
181 return fields;
182 }
183
184 private ResolveSet<SpiMetaField> createFields(JavaClass bcelClass) {
185 Field[] fields = bcelClass.getFields();
186 if (fields.length == 0) {
187 return NameSet.emptyNameSet();
188 }
189
190 SpiMetaField[] metaFields = new SpiMetaField[fields.length];
191 for(int i = 0; i<fields.length; ++i) {
192 metaFields[i]= new BcelField(fields[i]);
193 }
194
195 return new ResolveSet<SpiMetaField>(metaFields);
196 }
197
198 @Override
199 public boolean isAssignableFrom(MetaClass assignor) {
200 do {
201 if (isAssignableFromAnyInterface(assignor)) {
202 return true;
203 }
204 assignor = assignor.getParent();
205 }
206 while (assignor != null);
207 return false;
208 }
209
210 private boolean isAssignableFromAnyInterface(MetaClass assignor) {
211 if (equals(assignor)) {
212 return true;
213 }
214 for (MetaClass derived : assignor.getInterfaces()) {
215 if (isAssignableFromAnyInterface(derived)) {
216 return true;
217 }
218 }
219 return false;
220 }
221
222 @Override
223 public boolean equals(Object obj) {
224 if (!(obj instanceof MetaClass)) {
225 return false;
226 }
227 MetaClass other = (MetaClass) obj;
228 return canonicalName.equals(other.getName()) && location.equals(other.getClassLocation());
229 }
230
231 @Override
232 public int hashCode() {
233 final int prime = 33;
234 int result = 1;
235 result = prime * result + canonicalName.hashCode();
236 result = prime * result + location.hashCode();
237 return result;
238 }
239 }