blob: f9c396ee17ec8b925bc940be125946ca6da060c5 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.bnd.component;
2
3import java.lang.reflect.*;
4import java.util.*;
5import java.util.regex.*;
6
7import org.osgi.service.component.annotations.*;
8
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00009import aQute.bnd.osgi.*;
10import aQute.bnd.osgi.Clazz.MethodDef;
11import aQute.bnd.osgi.Descriptors.TypeRef;
Stuart McCulloch6a046662012-07-19 13:11:20 +000012import aQute.bnd.version.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000013import aQute.lib.collections.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000014
15/**
16 * fixup any unbind methods To declare no unbind method, the value "-" must be
17 * used. If not specified, the name of the unbind method is derived from the
18 * name of the annotated bind method. If the annotated method name begins with
19 * set, that is replaced with unset to derive the unbind method name. If the
20 * annotated method name begins with add, that is replaced with remove to derive
21 * the unbind method name. Otherwise, un is prefixed to the annotated method
22 * name to derive the unbind method name.
23 *
24 * @return
25 * @throws Exception
26 */
27public class AnnotationReader extends ClassDataCollector {
28 final static TypeRef[] EMPTY = new TypeRef[0];
29 final static Pattern PROPERTY_PATTERN = Pattern
Stuart McCullochd602fe12012-07-20 16:50:39 +000030 .compile("\\s*([^=\\s:]+)\\s*(?::\\s*(Boolean|Byte|Character|Short|Integer|Long|Float|Double|String)\\s*)?=(.*)");
Stuart McCullochbb014372012-06-07 21:57:32 +000031
Stuart McCullochec47fe72012-09-19 12:56:05 +000032 public static final Version V1_0 = new Version("1.0.0"); // "1.0.0"
Stuart McCullochbb014372012-06-07 21:57:32 +000033 public static final Version V1_1 = new Version("1.1.0"); // "1.1.0"
Stuart McCullochec47fe72012-09-19 12:56:05 +000034 public static final Version V1_2 = new Version("1.2.0"); // "1.2.0"
35// public static final Version V1_3 = new Version("1.3.0"); // "1.3.0"
Stuart McCullochbb014372012-06-07 21:57:32 +000036
Stuart McCullochec47fe72012-09-19 12:56:05 +000037 public static final String FELIX_1_2 = "http://felix.apache.org/xmlns/scr/v1.2.0-felix";
38
39 static Pattern BINDNAME = Pattern.compile("(set|add|bind)?(.*)");
40
41 static Pattern BINDDESCRIPTORDS10 = Pattern
42 .compile("\\(((L([^;]+);)|Lorg/osgi/framework/ServiceReference;)\\)V");
43 static Pattern BINDDESCRIPTORDS11 = Pattern
44 .compile("\\(((L([^;]+);)(Ljava/util/Map;)?|Lorg/osgi/framework/ServiceReference;)\\)V");
45 static Pattern BINDDESCRIPTORDS13 = Pattern
46 .compile("\\(((L([^;]+);)(Ljava/util/Map;)?|Lorg/osgi/framework/ServiceReference;)\\)Ljava/util/Map;");
47 static Pattern REFERENCEBINDDESCRIPTOR = Pattern
Stuart McCullochbb014372012-06-07 21:57:32 +000048 .compile("\\(Lorg/osgi/framework/ServiceReference;\\)V");
49
Stuart McCullochec47fe72012-09-19 12:56:05 +000050 static Pattern LIFECYCLEDESCRIPTORDS10 = Pattern
51 .compile("\\((Lorg/osgi/service/component/ComponentContext;)\\)V");
52 static Pattern LIFECYCLEDESCRIPTORDS11 = Pattern
53 .compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;))*\\)V");
54 static Pattern LIFECYCLEDESCRIPTORDS13 = Pattern
55 .compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;))*\\)Ljava/util/Map;");
56 static Pattern DEACTIVATEDESCRIPTORDS11 = Pattern
57 .compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;)|(Ljava/lang/Integer;)|(I))*\\)V");
58 static Pattern DEACTIVATEDESCRIPTORDS13 = Pattern
59 .compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;)|(Ljava/lang/Integer;)|(I))*\\)Ljava/util/Map;");
60
Stuart McCullochbb014372012-06-07 21:57:32 +000061 ComponentDef component = new ComponentDef();
62
63 Clazz clazz;
64 TypeRef interfaces[];
65 MethodDef method;
66 TypeRef className;
67 Analyzer analyzer;
Stuart McCulloch2286f232012-06-15 13:27:53 +000068 MultiMap<String,String> methods = new MultiMap<String,String>();
Stuart McCullochbb014372012-06-07 21:57:32 +000069 TypeRef extendsClass;
Stuart McCullochec47fe72012-09-19 12:56:05 +000070 final boolean inherit;
Stuart McCullochbb014372012-06-07 21:57:32 +000071 boolean baseclass = true;
Stuart McCullochec47fe72012-09-19 12:56:05 +000072
73 final boolean felixExtensions;
Stuart McCullochbb014372012-06-07 21:57:32 +000074
Stuart McCullochec47fe72012-09-19 12:56:05 +000075 AnnotationReader(Analyzer analyzer, Clazz clazz, boolean inherit, boolean felixExtensions) {
Stuart McCullochbb014372012-06-07 21:57:32 +000076 this.analyzer = analyzer;
77 this.clazz = clazz;
78 this.inherit = inherit;
Stuart McCullochec47fe72012-09-19 12:56:05 +000079 this.felixExtensions = felixExtensions;
Stuart McCullochbb014372012-06-07 21:57:32 +000080 }
81
82 public static ComponentDef getDefinition(Clazz c, Analyzer analyzer) throws Exception {
83 boolean inherit = Processor.isTrue(analyzer.getProperty("-dsannotations-inherit"));
Stuart McCullochec47fe72012-09-19 12:56:05 +000084 boolean felixExtensions = Processor.isTrue(analyzer.getProperty("-ds-felix-extensions"));
85 AnnotationReader r = new AnnotationReader(analyzer, c, inherit, felixExtensions);
Stuart McCullochbb014372012-06-07 21:57:32 +000086 return r.getDef();
87 }
88
89 private ComponentDef getDef() throws Exception {
90 clazz.parseClassFileWithCollector(this);
91 if (component.implementation == null)
92 return null;
93
94 if (inherit) {
95 baseclass = false;
96 while (extendsClass != null) {
97 if (extendsClass.isJava())
98 break;
99
100 Clazz ec = analyzer.findClass(extendsClass);
101 if (ec == null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000102 analyzer.error("Missing super class for DS annotations: " + extendsClass + " from "
103 + clazz.getClassName());
Stuart McCullochbb014372012-06-07 21:57:32 +0000104 } else {
105 ec.parseClassFileWithCollector(this);
106 }
107 }
108 }
109 for (ReferenceDef rdef : component.references.values()) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000110 rdef.unbind = referredMethod(analyzer, rdef, rdef.unbind, "add(.*)", "remove$1", "(.*)", "un$1");
111 rdef.updated = referredMethod(analyzer, rdef, rdef.updated, "(add|set|bind)(.*)", "updated$2", "(.*)",
112 "updated$1");
Stuart McCullochbb014372012-06-07 21:57:32 +0000113 }
114 return component;
115 }
116
117 /**
Stuart McCullochbb014372012-06-07 21:57:32 +0000118 * @param analyzer
119 * @param rdef
120 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000121 protected String referredMethod(Analyzer analyzer, ReferenceDef rdef, String value, String... matches) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000122 if (value == null) {
123 String bind = rdef.bind;
124 for (int i = 0; i < matches.length; i += 2) {
125 Matcher m = Pattern.compile(matches[i]).matcher(bind);
126 if (m.matches()) {
127 value = m.replaceFirst(matches[i + 1]);
128 break;
129 }
130 }
131 } else if (value.equals("-"))
132 return null;
133
134 if (methods.containsKey(value)) {
135 for (String descriptor : methods.get(value)) {
Stuart McCullochec47fe72012-09-19 12:56:05 +0000136 Matcher matcher = BINDDESCRIPTORDS10.matcher(descriptor);
Stuart McCullochbb014372012-06-07 21:57:32 +0000137 if (matcher.matches()) {
138 String type = matcher.group(2);
Stuart McCullochec47fe72012-09-19 12:56:05 +0000139 if (rdef.service.equals(Clazz.objectDescriptorToFQN(type))
Stuart McCullochbb014372012-06-07 21:57:32 +0000140 || type.equals("Lorg/osgi/framework/ServiceReference;")) {
141
142 return value;
143 }
144 }
Stuart McCullochec47fe72012-09-19 12:56:05 +0000145 matcher = BINDDESCRIPTORDS11.matcher(descriptor);
146 if (matcher.matches()) {
147 String type = matcher.group(2);
148 if (rdef.service.equals(Clazz.objectDescriptorToFQN(type))
149 || type.equals("Lorg/osgi/framework/ServiceReference;")) {
150 rdef.updateVersion(V1_1);
151 return value;
152 }
153 }
154 matcher = BINDDESCRIPTORDS13.matcher(descriptor);
155 if (felixExtensions && matcher.matches()) {
156 String type = matcher.group(2);
157 if (rdef.service.equals(Clazz.objectDescriptorToFQN(type))
158 || type.equals("Lorg/osgi/framework/ServiceReference;")) {
159 rdef.updateVersion(V1_2);
160 if (component.xmlns == null) {
161 component.xmlns = FELIX_1_2;
162 }
163 return value;
164 }
165 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000166 }
167 analyzer.error(
168 "A related method to %s from the reference %s has no proper prototype for class %s. Expected void %s(%s s [,Map m] | ServiceReference r)",
169 rdef.bind, value, component.implementation, value, rdef.service);
170 }
171 return null;
172 }
173
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000174 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +0000175 public void annotation(Annotation annotation) {
176 try {
177 java.lang.annotation.Annotation a = annotation.getAnnotation();
178 if (a instanceof Component)
179 doComponent((Component) a, annotation);
180 else if (a instanceof Activate)
181 doActivate();
182 else if (a instanceof Deactivate)
183 doDeactivate();
184 else if (a instanceof Modified)
185 doModified();
186 else if (a instanceof Reference)
187 doReference((Reference) a, annotation);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000188 }
189 catch (Exception e) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000190 e.printStackTrace();
191 analyzer.error("During generation of a component on class %s, exception %s", clazz, e);
192 }
193 }
194
195 /**
196 *
197 */
Stuart McCullochec47fe72012-09-19 12:56:05 +0000198 protected void doActivate() {
199 String methodDescriptor = method.getDescriptor().toString();
200 if ("activate".equals(method.getName()) && LIFECYCLEDESCRIPTORDS10.matcher(methodDescriptor).matches()) {
201 component.activate = method.getName();
202 } else if (LIFECYCLEDESCRIPTORDS11.matcher(methodDescriptor).matches()) {
203 component.activate = method.getName();
204 component.updateVersion(V1_1);
205 } else if (felixExtensions && LIFECYCLEDESCRIPTORDS13.matcher(methodDescriptor).matches()) {
206 component.activate = method.getName();
207 component.updateVersion(V1_2);
208 if (component.xmlns == null) {
209 component.xmlns = FELIX_1_2;
210 }
211 } else
Stuart McCullochbb014372012-06-07 21:57:32 +0000212 analyzer.error(
Stuart McCullochec47fe72012-09-19 12:56:05 +0000213 "Activate method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s",
Stuart McCullochbb014372012-06-07 21:57:32 +0000214 clazz, method.getDescriptor());
Stuart McCullochec47fe72012-09-19 12:56:05 +0000215
216 }
217
218 /**
219 *
220 */
221 protected void doDeactivate() {
222 String methodDescriptor = method.getDescriptor().toString();
223 if ( "deactivate".equals(method.getName()) && LIFECYCLEDESCRIPTORDS10.matcher(methodDescriptor).matches()) {
224 component.deactivate = method.getName();
225 } else if (DEACTIVATEDESCRIPTORDS11.matcher(methodDescriptor).matches()) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000226 component.deactivate = method.getName();
Stuart McCullochec47fe72012-09-19 12:56:05 +0000227 component.updateVersion(V1_1);
228 } else if (felixExtensions && DEACTIVATEDESCRIPTORDS13.matcher(methodDescriptor).matches()) {
229 component.deactivate = method.getName();
230 component.updateVersion(V1_2);
231 if (component.xmlns == null) {
232 component.xmlns = FELIX_1_2;
233 }
234 } else
235 analyzer.error(
236 "Deactivate method for %s does not have an acceptable prototype, only Map, ComponentContext, BundleContext, int, or Integer is allowed. Found: %s",
237 clazz, method.getDescriptor());
Stuart McCullochbb014372012-06-07 21:57:32 +0000238 }
239
240 /**
241 *
242 */
243 protected void doModified() {
Stuart McCullochec47fe72012-09-19 12:56:05 +0000244 if (LIFECYCLEDESCRIPTORDS11.matcher(method.getDescriptor().toString()).matches()) {
245 component.modified = method.getName();
246 component.updateVersion(V1_1);
247 } else if (felixExtensions && LIFECYCLEDESCRIPTORDS13.matcher(method.getDescriptor().toString()).matches()) {
248 component.modified = method.getName();
249 component.updateVersion(V1_2);
250 if (component.xmlns == null) {
251 component.xmlns = FELIX_1_2;
252 }
253 } else
254
Stuart McCullochbb014372012-06-07 21:57:32 +0000255 analyzer.error(
256 "Modified method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s",
257 clazz, method.getDescriptor());
Stuart McCullochbb014372012-06-07 21:57:32 +0000258 }
259
260 /**
261 * @param annotation
262 * @throws Exception
263 */
264 protected void doReference(Reference reference, Annotation raw) throws Exception {
265 ReferenceDef def = new ReferenceDef();
266 def.name = reference.name();
267
268 if (def.name == null) {
269 Matcher m = BINDNAME.matcher(method.getName());
Stuart McCulloch2286f232012-06-15 13:27:53 +0000270 if (m.matches())
Stuart McCullochbb014372012-06-07 21:57:32 +0000271 def.name = m.group(2);
272 else
273 analyzer.error("Invalid name for bind method %s", method.getName());
274 }
275
276 def.unbind = reference.unbind();
277 def.updated = reference.updated();
278 def.bind = method.getName();
279
280 def.service = raw.get("service");
281 if (def.service != null) {
282 def.service = Clazz.objectDescriptorToFQN(def.service);
283 } else {
284 // We have to find the type of the current method to
285 // link it to the referenced service.
Stuart McCullochec47fe72012-09-19 12:56:05 +0000286 String methodDescriptor = method.getDescriptor().toString();
287 Matcher m = BINDDESCRIPTORDS10.matcher(methodDescriptor);
Stuart McCullochbb014372012-06-07 21:57:32 +0000288 if (m.matches()) {
289 def.service = Descriptors.binaryToFQN(m.group(3));
Stuart McCullochec47fe72012-09-19 12:56:05 +0000290 } else {
291 m = BINDDESCRIPTORDS11.matcher(methodDescriptor);
292 if (m.matches()) {
293 def.service = Descriptors.binaryToFQN(m.group(3));
294 def.updateVersion(V1_1);
295 } else {
296 m = BINDDESCRIPTORDS13.matcher(methodDescriptor);
297 if (felixExtensions && m.matches()) {
298 def.service = Descriptors.binaryToFQN(m.group(3));
299 def.updateVersion(V1_2);
300 if (component.xmlns == null) {
301 component.xmlns = FELIX_1_2;
302 }
303 } else
304 throw new IllegalArgumentException(
305 "Cannot detect the type of a Component Reference from the descriptor: "
306 + method.getDescriptor());
307 }
308 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000309 }
310
311 // Check if we have a target, this must be a filter
312 def.target = reference.target();
313
314 if (component.references.containsKey(def.name))
315 analyzer.error(
316 "In component %s, multiple references with the same name: %s. Previous def: %s, this def: %s",
317 component.implementation, component.references.get(def.name), def.service, "");
318 else
319 component.references.put(def.name, def);
320
321 def.cardinality = reference.cardinality();
322 def.policy = reference.policy();
323 def.policyOption = reference.policyOption();
324 }
325
326 /**
Stuart McCullochbb014372012-06-07 21:57:32 +0000327 * @param annotation
328 * @throws Exception
329 */
330 protected void doComponent(Component comp, Annotation annotation) throws Exception {
331
332 // Check if we are doing a super class
333 if (component.implementation != null)
334 return;
335
Stuart McCullochbb014372012-06-07 21:57:32 +0000336 component.implementation = clazz.getClassName();
337 component.name = comp.name();
338 component.factory = comp.factory();
339 component.configurationPolicy = comp.configurationPolicy();
340 if (annotation.get("enabled") != null)
341 component.enabled = comp.enabled();
342 if (annotation.get("factory") != null)
343 component.factory = comp.factory();
344 if (annotation.get("immediate") != null)
345 component.immediate = comp.immediate();
346 if (annotation.get("servicefactory") != null)
347 component.servicefactory = comp.servicefactory();
348
Stuart McCullochec47fe72012-09-19 12:56:05 +0000349 if (annotation.get("configurationPid") != null) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000350 component.configurationPid = comp.configurationPid();
Stuart McCullochec47fe72012-09-19 12:56:05 +0000351 component.updateVersion(V1_2);
352 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000353
354 if (annotation.get("xmlns") != null)
355 component.xmlns = comp.xmlns();
356
357 String properties[] = comp.properties();
358 if (properties != null)
359 for (String entry : properties)
360 component.properties.add(entry);
361
362 doProperties(comp.property());
363 Object[] x = annotation.get("service");
364
365 if (x == null) {
366 // Use the found interfaces, but convert from internal to
367 // fqn.
368 if (interfaces != null) {
369 List<TypeRef> result = new ArrayList<TypeRef>();
370 for (int i = 0; i < interfaces.length; i++) {
371 if (!interfaces[i].equals(analyzer.getTypeRef("scala/ScalaObject")))
372 result.add(interfaces[i]);
373 }
374 component.service = result.toArray(EMPTY);
375 }
376 } else {
377 // We have explicit interfaces set
378 component.service = new TypeRef[x.length];
379 for (int i = 0; i < x.length; i++) {
380 String s = (String) x[i];
381 TypeRef ref = analyzer.getTypeRefFromFQN(s);
382 component.service[i] = ref;
383 }
384 }
385
386 }
387
388 /**
389 * Parse the properties
390 */
391
392 private void doProperties(String[] properties) {
393 if (properties != null) {
394 for (String p : properties) {
395 Matcher m = PROPERTY_PATTERN.matcher(p);
396
397 if (m.matches()) {
398 String key = m.group(1);
Stuart McCulloch6a046662012-07-19 13:11:20 +0000399 String type = m.group(2);
400 if ( type != null)
401 key += ":" + type;
402
403 String value = m.group(3);
Stuart McCullochbb014372012-06-07 21:57:32 +0000404 component.property.add(key, value);
405 } else
Stuart McCulloch2286f232012-06-15 13:27:53 +0000406 throw new IllegalArgumentException("Malformed property '" + p + "' on component: " + className);
Stuart McCullochbb014372012-06-07 21:57:32 +0000407 }
408 }
409 }
410
411 /**
412 * Are called during class parsing
413 */
414
Stuart McCulloch2286f232012-06-15 13:27:53 +0000415 @Override
416 public void classBegin(int access, TypeRef name) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000417 className = name;
418 }
419
Stuart McCulloch2286f232012-06-15 13:27:53 +0000420 @Override
421 public void implementsInterfaces(TypeRef[] interfaces) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000422 this.interfaces = interfaces;
423 }
424
Stuart McCulloch2286f232012-06-15 13:27:53 +0000425 @Override
426 public void method(Clazz.MethodDef method) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000427 int access = method.getAccess();
428
429 if (Modifier.isAbstract(access) || Modifier.isStatic(access))
430 return;
431
432 if (!baseclass && Modifier.isPrivate(access))
433 return;
434
435 this.method = method;
436 methods.add(method.getName(), method.getDescriptor().toString());
437 }
438
Stuart McCulloch2286f232012-06-15 13:27:53 +0000439 @Override
440 public void extendsClass(TypeRef name) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000441 this.extendsClass = name;
442 }
443
444}