Lines 39-55
Link Here
|
39 |
|
39 |
|
40 |
package org.netbeans.modules.projectapi; |
40 |
package org.netbeans.modules.projectapi; |
41 |
|
41 |
|
|
|
42 |
import java.util.ArrayList; |
43 |
import java.util.List; |
44 |
import java.util.Map; |
42 |
import java.util.Set; |
45 |
import java.util.Set; |
43 |
import javax.annotation.processing.Processor; |
46 |
import javax.annotation.processing.Processor; |
44 |
import javax.annotation.processing.RoundEnvironment; |
47 |
import javax.annotation.processing.RoundEnvironment; |
45 |
import javax.annotation.processing.SupportedAnnotationTypes; |
48 |
import javax.annotation.processing.SupportedAnnotationTypes; |
46 |
import javax.annotation.processing.SupportedSourceVersion; |
49 |
import javax.annotation.processing.SupportedSourceVersion; |
47 |
import javax.lang.model.SourceVersion; |
50 |
import javax.lang.model.SourceVersion; |
|
|
51 |
import javax.lang.model.element.AnnotationMirror; |
52 |
import javax.lang.model.element.AnnotationValue; |
48 |
import javax.lang.model.element.Element; |
53 |
import javax.lang.model.element.Element; |
|
|
54 |
import javax.lang.model.element.ElementKind; |
55 |
import javax.lang.model.element.ExecutableElement; |
56 |
import javax.lang.model.element.Modifier; |
49 |
import javax.lang.model.element.TypeElement; |
57 |
import javax.lang.model.element.TypeElement; |
|
|
58 |
import javax.lang.model.element.VariableElement; |
59 |
import javax.lang.model.type.DeclaredType; |
60 |
import javax.lang.model.type.TypeMirror; |
61 |
import javax.lang.model.util.ElementFilter; |
62 |
import org.netbeans.api.project.Project; |
63 |
import org.netbeans.spi.project.LookupMerger; |
50 |
import org.netbeans.spi.project.LookupProvider; |
64 |
import org.netbeans.spi.project.LookupProvider; |
|
|
65 |
import org.netbeans.spi.project.ProjectServiceProvider; |
66 |
import org.openide.filesystems.annotations.LayerBuilder; |
51 |
import org.openide.filesystems.annotations.LayerGeneratingProcessor; |
67 |
import org.openide.filesystems.annotations.LayerGeneratingProcessor; |
52 |
import org.openide.filesystems.annotations.LayerGenerationException; |
68 |
import org.openide.filesystems.annotations.LayerGenerationException; |
|
|
69 |
import org.openide.util.Lookup; |
53 |
import org.openide.util.lookup.ServiceProvider; |
70 |
import org.openide.util.lookup.ServiceProvider; |
54 |
|
71 |
|
55 |
/** |
72 |
/** |
Lines 58-64
Link Here
|
58 |
*/ |
75 |
*/ |
59 |
@ServiceProvider(service=Processor.class) |
76 |
@ServiceProvider(service=Processor.class) |
60 |
@SupportedSourceVersion(SourceVersion.RELEASE_6) |
77 |
@SupportedSourceVersion(SourceVersion.RELEASE_6) |
61 |
@SupportedAnnotationTypes("org.netbeans.spi.project.LookupProvider.Registration") |
78 |
@SupportedAnnotationTypes({ |
|
|
79 |
"org.netbeans.spi.project.LookupProvider.Registration", |
80 |
"org.netbeans.spi.project.ProjectServiceProvider", |
81 |
"org.netbeans.spi.project.LookupMerger.Registration" |
82 |
}) |
62 |
public class LookupProviderAnnotationProcessor extends LayerGeneratingProcessor { |
83 |
public class LookupProviderAnnotationProcessor extends LayerGeneratingProcessor { |
63 |
|
84 |
|
64 |
protected boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws LayerGenerationException { |
85 |
protected boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws LayerGenerationException { |
Lines 77-83
Link Here
|
77 |
layer(e).instanceFile("Projects/" + type.id() + "/Lookup", null, LookupProvider.class).position(type.position()).write(); |
98 |
layer(e).instanceFile("Projects/" + type.id() + "/Lookup", null, LookupProvider.class).position(type.position()).write(); |
78 |
} |
99 |
} |
79 |
} |
100 |
} |
|
|
101 |
for (Element e : roundEnv.getElementsAnnotatedWith(ProjectServiceProvider.class)) { |
102 |
List<TypeMirror> services = findServiceAnnotation(e); |
103 |
if (services.isEmpty()) { |
104 |
throw new LayerGenerationException("Must specify at least one service", e); |
105 |
} |
106 |
String servicesBinName = null; |
107 |
for (TypeMirror service : services) { |
108 |
String n = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(service)).toString(); |
109 |
if (n.equals(LookupMerger.class.getName())) { |
110 |
throw new LayerGenerationException("@ProjectServiceProvider should not be used on LookupMerger; use @LookupMerger.Registration instead", e); |
111 |
} |
112 |
servicesBinName = servicesBinName == null ? n : servicesBinName + "," + n; |
113 |
} |
114 |
String[] binAndMethodNames = findPSPDefinition(e, services); |
115 |
ProjectServiceProvider psp = e.getAnnotation(ProjectServiceProvider.class); |
116 |
if (psp.projectType().length == 0 && psp.projectTypes().length == 0) { |
117 |
throw new LayerGenerationException("You must specify either projectType or projectTypes", e); |
118 |
} |
119 |
String fileBaseName = binAndMethodNames[0].replace('.', '-'); |
120 |
if (binAndMethodNames[1] != null) { |
121 |
fileBaseName += "-" + binAndMethodNames[1]; |
122 |
} |
123 |
for (String type : psp.projectType()) { |
124 |
LayerBuilder.File f = layer(e).file("Projects/" + type + "/Lookup/" + fileBaseName + ".instance"). |
125 |
methodvalue("instanceCreate", LazyLookupProviders.class.getName(), "forProjectServiceProvider"). |
126 |
stringvalue("class", binAndMethodNames[0]). |
127 |
stringvalue("service", servicesBinName); |
128 |
if (binAndMethodNames[1] != null) { |
129 |
f.stringvalue("method", binAndMethodNames[1]); |
130 |
} |
131 |
f.write(); |
132 |
} |
133 |
for (LookupProvider.Registration.ProjectType type : psp.projectTypes()) { |
134 |
LayerBuilder.File f = layer(e).file("Projects/" + type.id() + "/Lookup/" + fileBaseName + ".instance"). |
135 |
methodvalue("instanceCreate", LazyLookupProviders.class.getName(), "forProjectServiceProvider"). |
136 |
stringvalue("class", binAndMethodNames[0]). |
137 |
stringvalue("service", servicesBinName). |
138 |
position(type.position()); |
139 |
if (binAndMethodNames[1] != null) { |
140 |
f.stringvalue("method", binAndMethodNames[1]); |
141 |
} |
142 |
f.write(); |
143 |
} |
144 |
} |
145 |
for (Element e : roundEnv.getElementsAnnotatedWith(LookupMerger.Registration.class)) { |
146 |
LookupMerger.Registration lmr = e.getAnnotation(LookupMerger.Registration.class); |
147 |
String fileBaseName; |
148 |
DeclaredType impl; |
149 |
if (e.getKind() == ElementKind.CLASS) { |
150 |
fileBaseName = processingEnv.getElementUtils().getBinaryName((TypeElement) e).toString().replace('.', '-'); |
151 |
impl = (DeclaredType) e.asType(); |
152 |
} else { |
153 |
fileBaseName = processingEnv.getElementUtils().getBinaryName((TypeElement) e.getEnclosingElement()).toString().replace('.', '-') + |
154 |
"-" + e.getSimpleName().toString(); |
155 |
impl = (DeclaredType) ((ExecutableElement) e).getReturnType(); |
156 |
} |
157 |
DeclaredType service = findLookupMergerType(impl); |
158 |
if (service == null) { |
159 |
throw new LayerGenerationException("Not assignable to LookupMerger<T> for some T", e); |
160 |
} |
161 |
String serviceBinName = processingEnv.getElementUtils().getBinaryName((TypeElement) service.asElement()).toString(); |
162 |
if (lmr.projectType().length == 0 && lmr.projectTypes().length == 0) { |
163 |
throw new LayerGenerationException("You must specify either projectType or projectTypes", e); |
164 |
} |
165 |
for (String type : lmr.projectType()) { |
166 |
layer(e).file("Projects/" + type + "/Lookup/" + fileBaseName + ".instance"). |
167 |
methodvalue("instanceCreate", LazyLookupProviders.class.getName(), "forLookupMerger"). |
168 |
instanceAttribute("lookupMergerInstance", LookupMerger.class). |
169 |
stringvalue("service", serviceBinName). |
170 |
write(); |
171 |
} |
172 |
for (LookupProvider.Registration.ProjectType type : lmr.projectTypes()) { |
173 |
layer(e).file("Projects/" + type.id() + "/Lookup/" + fileBaseName + ".instance"). |
174 |
methodvalue("instanceCreate", LazyLookupProviders.class.getName(), "forLookupMerger"). |
175 |
instanceAttribute("lookupMergerInstance", LookupMerger.class). |
176 |
stringvalue("service", serviceBinName). |
177 |
position(type.position()). |
178 |
write(); |
179 |
} |
180 |
} |
80 |
return true; |
181 |
return true; |
81 |
} |
182 |
} |
82 |
|
183 |
|
|
|
184 |
private List<TypeMirror> findServiceAnnotation(Element e) throws LayerGenerationException { |
185 |
for (AnnotationMirror ann : e.getAnnotationMirrors()) { |
186 |
if (!ProjectServiceProvider.class.getName().equals(ann.getAnnotationType().toString())) { |
187 |
continue; |
188 |
} |
189 |
for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> attr : ann.getElementValues().entrySet()) { |
190 |
if (!attr.getKey().getSimpleName().contentEquals("service")) { |
191 |
continue; |
192 |
} |
193 |
List<TypeMirror> r = new ArrayList<TypeMirror>(); |
194 |
for (Object item : (List<?>) attr.getValue().getValue()) { |
195 |
r.add((TypeMirror) ((AnnotationValue) item).getValue()); |
196 |
} |
197 |
return r; |
198 |
} |
199 |
throw new LayerGenerationException("No service attr found", e); |
200 |
} |
201 |
throw new LayerGenerationException("No @ProjectServiceProvider found", e); |
202 |
} |
203 |
|
204 |
private String[] findPSPDefinition(Element e, List<TypeMirror> services) throws LayerGenerationException { |
205 |
if (e.getKind() == ElementKind.CLASS) { |
206 |
TypeElement clazz = (TypeElement) e; |
207 |
for (TypeMirror service : services) { |
208 |
if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), service)) { |
209 |
throw new LayerGenerationException("Not assignable to " + service, e); |
210 |
} |
211 |
} |
212 |
int constructorCount = 0; |
213 |
CONSTRUCTOR: for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) { |
214 |
if (!constructor.getModifiers().contains(Modifier.PUBLIC)) { |
215 |
continue; |
216 |
} |
217 |
List<? extends VariableElement> params = constructor.getParameters(); |
218 |
if (params.size() > 2) { |
219 |
continue; |
220 |
} |
221 |
for (VariableElement param : params) { |
222 |
if (!param.asType().equals(processingEnv.getElementUtils().getTypeElement(Project.class.getCanonicalName()).asType()) && |
223 |
!param.asType().equals(processingEnv.getElementUtils().getTypeElement(Lookup.class.getCanonicalName()).asType())) { |
224 |
continue CONSTRUCTOR; |
225 |
} |
226 |
} |
227 |
constructorCount++; |
228 |
} |
229 |
if (constructorCount != 1) { |
230 |
throw new LayerGenerationException("Must have exactly one public constructor optionally taking Project and/or Lookup", e); |
231 |
} |
232 |
return new String[] {processingEnv.getElementUtils().getBinaryName(clazz).toString(), null}; |
233 |
} else { |
234 |
ExecutableElement meth = (ExecutableElement) e; |
235 |
for (TypeMirror service : services) { |
236 |
if (!processingEnv.getTypeUtils().isAssignable(meth.getReturnType(), service)) { |
237 |
throw new LayerGenerationException("Not assignable to " + service, e); |
238 |
} |
239 |
} |
240 |
if (!meth.getModifiers().contains(Modifier.PUBLIC)) { |
241 |
throw new LayerGenerationException("Method must be public", e); |
242 |
} |
243 |
if (!meth.getModifiers().contains(Modifier.STATIC)) { |
244 |
throw new LayerGenerationException("Method must be static", e); |
245 |
} |
246 |
List<? extends VariableElement> params = meth.getParameters(); |
247 |
if (params.size() > 2) { |
248 |
throw new LayerGenerationException("Method must take at most two parameters", e); |
249 |
} |
250 |
for (VariableElement param : params) { |
251 |
if (!param.asType().equals(processingEnv.getElementUtils().getTypeElement(Project.class.getCanonicalName()).asType()) && |
252 |
!param.asType().equals(processingEnv.getElementUtils().getTypeElement(Lookup.class.getCanonicalName()).asType())) { |
253 |
throw new LayerGenerationException("Method parameters may be either Lookup or Project", e); |
254 |
} |
255 |
} |
256 |
return new String[] { |
257 |
processingEnv.getElementUtils().getBinaryName((TypeElement) meth.getEnclosingElement()).toString(), |
258 |
meth.getSimpleName().toString()}; |
259 |
} |
260 |
} |
261 |
|
262 |
private DeclaredType findLookupMergerType(DeclaredType t) { |
263 |
String rawName = processingEnv.getTypeUtils().erasure(t).toString(); |
264 |
if (rawName.equals(LookupMerger.class.getName())) { |
265 |
List<? extends TypeMirror> args = t.getTypeArguments(); |
266 |
if (args.size() == 1) { |
267 |
return (DeclaredType) args.get(0); |
268 |
} else { |
269 |
return null; |
270 |
} |
271 |
} |
272 |
for (TypeMirror supe : processingEnv.getTypeUtils().directSupertypes(t)) { |
273 |
DeclaredType result = findLookupMergerType((DeclaredType) supe); |
274 |
if (result != null) { |
275 |
return result; |
276 |
} |
277 |
} |
278 |
return null; |
279 |
} |
280 |
|
83 |
} |
281 |
} |