aboutsummaryrefslogtreecommitdiff
path: root/src/lombok/eclipse/HandlerLibrary.java
blob: 6ee2baaa1ce2efc79bab88c1978e8876fbcd6652 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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<T extends Annotation> {
		private EclipseAnnotationHandler<T> handler;
		private Class<T> annotationClass;
		
		AnnotationHandlerContainer(EclipseAnnotationHandler<T> handler, Class<T> annotationClass) {
			this.handler = handler;
			this.annotationClass = annotationClass;
		}
		
		public boolean handle(org.eclipse.jdt.internal.compiler.ast.Annotation annotation,
				final Node annotationNode) {
			AnnotationValues<T> annValues = Eclipse.createAnnotation(annotationClass, annotationNode);
			return handler.handle(annValues, annotation, annotationNode);
		}
	}
	
	private Map<String, AnnotationHandlerContainer<?>> annotationHandlers =
		new HashMap<String, AnnotationHandlerContainer<?>>();
	
	private Collection<EclipseASTVisitor> visitorHandlers = new ArrayList<EclipseASTVisitor>();

	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<EclipseAnnotationHandler> it;
		try {
			it = SpiLoadUtil.findServices(EclipseAnnotationHandler.class);
		} catch ( Throwable t ) {
			throw Lombok.sneakyThrow(t);
		}
		
		while ( it.hasNext() ) {
			try {
				EclipseAnnotationHandler<?> handler = it.next();
				Class<? extends Annotation> 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<EclipseASTVisitor> 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<String> 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;
	}
}