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    }