package lombok.eclipse; import static lombok.eclipse.Eclipse.toQualifiedName; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import lombok.Lombok; import lombok.core.AnnotationValues; import lombok.core.PrintAST; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; import lombok.core.TypeResolver; import lombok.core.AnnotationValues.AnnotationValueDecodeFail; import lombok.eclipse.EclipseAST.Node; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; public class HandlerLibrary { private TypeLibrary typeLibrary = new TypeLibrary(); private static class AnnotationHandlerContainer { private EclipseAnnotationHandler handler; private Class annotationClass; AnnotationHandlerContainer(EclipseAnnotationHandler handler, Class annotationClass) { this.handler = handler; this.annotationClass = annotationClass; } public boolean handle(org.eclipse.jdt.internal.compiler.ast.Annotation annotation, final Node annotationNode) { AnnotationValues annValues = Eclipse.createAnnotation(annotationClass, annotationNode); return handler.handle(annValues, annotation, annotationNode); } } private Map> annotationHandlers = new HashMap>(); private Collection visitorHandlers = new ArrayList(); private boolean skipPrintAST; public static HandlerLibrary load() { HandlerLibrary lib = new HandlerLibrary(); loadAnnotationHandlers(lib); loadVisitorHandlers(lib); return lib; } @SuppressWarnings("unchecked") private static void loadAnnotationHandlers(HandlerLibrary lib) { Iterator it; try { it = SpiLoadUtil.findServices(EclipseAnnotationHandler.class); } catch ( Throwable t ) { throw Lombok.sneakyThrow(t); } while ( it.hasNext() ) { try { EclipseAnnotationHandler handler = it.next(); Class annotationClass = SpiLoadUtil.findAnnotationClass(handler.getClass(), EclipseAnnotationHandler.class); AnnotationHandlerContainer container = new AnnotationHandlerContainer(handler, annotationClass); if ( lib.annotationHandlers.put(container.annotationClass.getName(), container) != null ) { Eclipse.error(null, "Duplicate handlers for annotation type: " + container.annotationClass.getName()); } lib.typeLibrary.addType(container.annotationClass.getName()); } catch ( Throwable t ) { Eclipse.error(null, "Can't load Lombok annotation handler for eclipse: ", t); } } } private static void loadVisitorHandlers(HandlerLibrary lib) { Iterator it; try { it = SpiLoadUtil.findServices(EclipseASTVisitor.class); } catch ( Throwable t ) { throw Lombok.sneakyThrow(t); } while ( it.hasNext() ) { try { lib.visitorHandlers.add(it.next()); } catch ( Throwable t ) { Eclipse.error(null, "Can't load Lombok visitor handler for eclipse: ", t); } } } public boolean handle(CompilationUnitDeclaration ast, EclipseAST.Node annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation) { String pkgName = annotationNode.getPackageDeclaration(); Collection imports = annotationNode.getImportStatements(); TypeResolver resolver = new TypeResolver(typeLibrary, pkgName, imports); TypeReference rawType = annotation.type; if ( rawType == null ) return false; boolean handled = false; for ( String fqn : resolver.findTypeMatches(annotationNode, toQualifiedName(annotation.type.getTypeName())) ) { boolean isPrintAST = fqn.equals(PrintAST.class.getName()); if ( isPrintAST == skipPrintAST ) continue; AnnotationHandlerContainer container = annotationHandlers.get(fqn); if ( container == null ) continue; try { handled |= container.handle(annotation, annotationNode); } catch ( AnnotationValueDecodeFail fail ) { fail.owner.setError(fail.getMessage(), fail.idx); } catch ( Throwable t ) { Eclipse.error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); } } return handled; } public void callASTVisitors(EclipseAST ast) { for ( EclipseASTVisitor visitor : visitorHandlers ) try { ast.traverse(visitor); } catch ( Throwable t ) { Eclipse.error((CompilationUnitDeclaration) ast.top().get(), String.format("Lombok visitor handler %s failed", visitor.getClass()), t); } } public void skipPrintAST() { skipPrintAST = true; } public void skipAllButPrintAST() { skipPrintAST = false; } }