blob: 73460496f76eb553e9b0790ae03396d4ef21ad65 [file] [log] [blame]
/*
* 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;
}
}