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 }