| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.felix.ipojo.manipulator.metadata.annotation; |
| |
| import org.apache.felix.ipojo.manipulator.Reporter; |
| import org.apache.felix.ipojo.manipulator.metadata.annotation.registry.BindingRegistry; |
| import org.apache.felix.ipojo.metadata.Attribute; |
| import org.apache.felix.ipojo.metadata.Element; |
| import org.objectweb.asm.*; |
| import org.objectweb.asm.tree.ClassNode; |
| import org.objectweb.asm.tree.FieldNode; |
| import org.objectweb.asm.tree.MethodNode; |
| |
| /** |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public class ClassMetadataCollector extends ClassVisitor { |
| |
| /** |
| * Binding's registry. |
| */ |
| private BindingRegistry registry; |
| |
| /** |
| * Output information. |
| */ |
| private Reporter reporter; |
| |
| /** |
| * Workbench where produced Elements will be merged and hierarchically organized. |
| */ |
| private ComponentWorkbench workbench; |
| |
| /** |
| * Class currently being analyzed. |
| */ |
| private ClassNode node; |
| |
| private Element componentMetadata; |
| |
| private Element instanceMetadata; |
| |
| public ClassMetadataCollector(BindingRegistry registry, Reporter reporter) { |
| super(Opcodes.ASM5); |
| this.registry = registry; |
| this.reporter = reporter; |
| node = new ClassNode(); |
| } |
| |
| /** |
| * Build metadata. May be {@literal null} if no "component type" was found. |
| * |
| * @return Build metadata. May be {@literal null} if no "component type" was found. |
| */ |
| public Element getComponentMetadata() { |
| return componentMetadata; |
| } |
| |
| /** |
| * Build instance metadata. May be {@literal null} if no "component type" was found. |
| * |
| * @return Build metadata. May be {@literal null} if no "component type" was found. |
| */ |
| public Element getInstanceMetadata() { |
| return instanceMetadata; |
| } |
| |
| @Override |
| public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
| node.visit(version, access, name, signature, superName, interfaces); |
| workbench = new ComponentWorkbench(registry, node); |
| } |
| |
| /** |
| * Visit class annotations. |
| * This method detects @component and @provides annotations. |
| * |
| * @param desc : annotation descriptor. |
| * @param visible : is the annotation visible at runtime. |
| * @return the annotation visitor. |
| * @see org.objectweb.asm.ClassVisitor#visitAnnotation(java.lang.String, boolean) |
| */ |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
| //TODO we should find a better way to do this. |
| // Cannot retrieve the class object as @Configuration is in iPOJO runtime. |
| if (Type.getType(desc).getClassName().equals("org.apache.felix.ipojo.configuration.Configuration")) { |
| workbench.ignore(true); |
| return null; |
| } |
| |
| // Return the visitor to be executed (may be null) |
| return registry.selection(workbench) |
| .type(this, node) |
| .annotatedWith(desc) |
| .get(); |
| |
| } |
| |
| /** |
| * Visit a field. |
| * Call the field collector visitor. |
| * |
| * @param access : field access. |
| * @param name : field name |
| * @param desc : field descriptor |
| * @param signature : field signature |
| * @param value : field value (static field only) |
| * @return the field visitor. |
| * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, |
| * java.lang.Object) |
| */ |
| public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
| return new FieldMetadataCollector(workbench, new FieldNode(access, name, desc, signature, value)); |
| } |
| |
| /** |
| * Visit a method. |
| * Call the method collector visitor. |
| * |
| * @param access : method access |
| * @param name : method name |
| * @param desc : method descriptor |
| * @param signature : method signature |
| * @param exceptions : method exceptions |
| * @return the Method Visitor. |
| * @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) |
| */ |
| public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| return new MethodMetadataCollector(workbench, new MethodNode(access, name, desc, signature, exceptions), reporter); |
| } |
| |
| /** |
| * End of the visit : compute final elements. |
| * |
| * @see org.objectweb.asm.ClassVisitor#visitEnd() |
| */ |
| @Override |
| public void visitEnd() { |
| // Only process real class (no annotations, no interfaces) |
| if (!(is(Opcodes.ACC_ANNOTATION) || is(Opcodes.ACC_INTERFACE) || is(Opcodes.ACC_ABSTRACT))) { |
| if (workbench.getRoot() == null) { |
| // No 'top-level' element has been contributed |
| |
| if (workbench.ignore()) { |
| // Ignore this class. |
| return; |
| } |
| |
| if (!workbench.getElements().isEmpty()) { |
| // There are other annotation's contribution on this type (additional handler declaration/configuration) |
| // That means that there is a missing 'component type' annotation |
| |
| reporter.warn("Class %s has not been marked as a component type (no @Component, @Handler, " + |
| "...). It will be ignored by the iPOJO manipulator.", |
| workbench.getType().getClassName() |
| ); |
| return; |
| } // else: no root and no elements |
| return; |
| } |
| |
| componentMetadata = workbench.build(); |
| instanceMetadata = workbench.getInstance(); |
| |
| // If we have an instance declared and the component metadata has a name, we update the component's attribute |
| // of the instance (https://issues.apache.org/jira/browse/FELIX-4052). |
| if (componentMetadata != null && componentMetadata.containsAttribute("name") && instanceMetadata != null) { |
| // Update the component attribute |
| instanceMetadata.addAttribute(new Attribute("component", componentMetadata.getAttribute("name"))); |
| } |
| |
| } |
| } |
| |
| private boolean is(int flags) { |
| return (node.access & flags) == flags; |
| } |
| |
| } |