/**
* AnalyzerBeans
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.eobjects.analyzer.descriptors;
import java.lang.annotation.Annotation;
import org.eobjects.analyzer.beans.api.Analyzer;
import org.eobjects.analyzer.beans.api.AnalyzerBean;
import org.eobjects.analyzer.beans.api.Filter;
import org.eobjects.analyzer.beans.api.FilterBean;
import org.eobjects.analyzer.beans.api.Renderer;
import org.eobjects.analyzer.beans.api.RendererBean;
import org.eobjects.analyzer.beans.api.RenderingFormat;
import org.eobjects.analyzer.beans.api.Transformer;
import org.eobjects.analyzer.beans.api.TransformerBean;
import org.eobjects.analyzer.util.ReflectionUtils;
import org.apache.metamodel.util.Predicate;
import org.apache.metamodel.util.TruePredicate;
import org.kohsuke.asm5.AnnotationVisitor;
import org.kohsuke.asm5.Attribute;
import org.kohsuke.asm5.ClassVisitor;
import org.kohsuke.asm5.FieldVisitor;
import org.kohsuke.asm5.MethodVisitor;
import org.kohsuke.asm5.Opcodes;
import org.kohsuke.asm5.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ASM class visitor for analyzer beans components.
*/
final class BeanClassVisitor extends ClassVisitor {
private static final int API_VERSION = Opcodes.ASM4;
private final static Logger _logger = LoggerFactory.getLogger(BeanClassVisitor.class);
private final ClassLoader _classLoader;
private final Predicate<Class<? extends RenderingFormat<?>>> _renderingFormatPredicate;
private Class<?> _beanClazz;
private String _name;
public BeanClassVisitor(ClassLoader classLoader,
Predicate<Class<? extends RenderingFormat<?>>> renderingFormatPredicate) {
super(API_VERSION);
_classLoader = classLoader;
_renderingFormatPredicate = renderingFormatPredicate;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
_name = name;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (isAnnotation(desc, RendererBean.class)) {
if (_renderingFormatPredicate == null || _renderingFormatPredicate instanceof TruePredicate) {
initializeClass();
return null;
}
return new AnnotationVisitor(API_VERSION) {
@Override
public void visit(String name, Object value) {
final Type valueType = (Type) value;
final String renderingFormatClassName = valueType.getClassName();
final Class<? extends RenderingFormat<?>> renderingFormatClass;
try {
@SuppressWarnings("unchecked")
final Class<? extends RenderingFormat<?>> cls = (Class<? extends RenderingFormat<?>>) Class
.forName(renderingFormatClassName, false, _classLoader);
renderingFormatClass = cls;
} catch (Exception e) {
if (_logger.isWarnEnabled()) {
_logger.warn("Failed to read rendering format of renderer class '"
+ renderingFormatClassName + "', ignoring: " + _name, e);
}
return;
}
final Boolean proceed = _renderingFormatPredicate.eval(renderingFormatClass);
if (proceed == null || !proceed.booleanValue()) {
_logger.info("Skipping renderer because it's format was not accepted by predicate: {}", _name);
return;
}
initializeClass();
}
};
}
if (isAnnotation(desc, AnalyzerBean.class) || isAnnotation(desc, TransformerBean.class)
|| isAnnotation(desc, FilterBean.class)) {
initializeClass();
}
return null;
}
private boolean isAnnotation(String annotationDesc, Class<? extends Annotation> annotationClass) {
return annotationDesc.indexOf(annotationClass.getName().replace('.', '/')) != -1;
}
private Class<?> initializeClass() {
if (_beanClazz == null) {
String javaName = _name.replace('/', '.');
try {
_beanClazz = Class.forName(javaName, true, _classLoader);
} catch (ClassNotFoundException e) {
// This happens when the class itself does not exist
_logger.error("Could not find class to be loaded: " + javaName, e);
} catch (NoClassDefFoundError e) {
// This happens if the class depends on a unsatisfied
// dependency. For instance when it is a renderer bean that
// depends on a particular rendering format. We will gracefully
// recover from this scenario with just a warning.
_logger.error("Failed to load class {} because of unsatisfied class dependency: {}", javaName,
e.getMessage());
if (_logger.isDebugEnabled()) {
_logger.debug("Failed to load class: " + javaName, e);
}
}
}
return _beanClazz;
}
public boolean isAnalyzer() {
if (_beanClazz != null) {
return ReflectionUtils.isAnnotationPresent(_beanClazz, AnalyzerBean.class)
&& ReflectionUtils.is(_beanClazz, Analyzer.class);
}
return false;
}
public boolean isTransformer() {
if (_beanClazz != null) {
return ReflectionUtils.isAnnotationPresent(_beanClazz, TransformerBean.class)
&& ReflectionUtils.is(_beanClazz, Transformer.class);
}
return false;
}
public boolean isRenderer() {
if (_beanClazz != null) {
return ReflectionUtils.isAnnotationPresent(_beanClazz, RendererBean.class)
&& ReflectionUtils.is(_beanClazz, Renderer.class);
}
return false;
}
public boolean isFilter() {
if (_beanClazz != null) {
return ReflectionUtils.isAnnotationPresent(_beanClazz, FilterBean.class)
&& ReflectionUtils.is(_beanClazz, Filter.class);
}
return false;
}
public Class<?> getBeanClass() {
return _beanClazz;
}
@Override
public void visitAttribute(Attribute arg0) {
}
@Override
public void visitEnd() {
}
@Override
public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) {
return null;
}
@Override
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
@Override
public MethodVisitor visitMethod(int arg0, String arg1, String arg2, String arg3, String[] arg4) {
return null;
}
@Override
public void visitOuterClass(String arg0, String arg1, String arg2) {
}
@Override
public void visitSource(String arg0, String arg1) {
}
}