blob: 978571c3e0b15219f9c64fa2d08844692623470c [file] [log] [blame]
Pierre De Rop3a00a212015-03-01 09:27:46 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.annotation.plugin.bnd;
20
21import java.io.PrintWriter;
22import java.util.ArrayList;
23import java.util.Arrays;
Pierre De Rop40ecace2016-02-09 21:44:38 +000024import java.util.Dictionary;
Pierre De Rop3a00a212015-03-01 09:27:46 +000025import java.util.HashSet;
26import java.util.List;
27import java.util.Set;
28
29import org.apache.felix.dm.annotation.api.AdapterService;
30import org.apache.felix.dm.annotation.api.AspectService;
31import org.apache.felix.dm.annotation.api.BundleAdapterService;
32import org.apache.felix.dm.annotation.api.BundleDependency;
33import org.apache.felix.dm.annotation.api.Component;
34import org.apache.felix.dm.annotation.api.Composition;
35import org.apache.felix.dm.annotation.api.ConfigurationDependency;
36import org.apache.felix.dm.annotation.api.Destroy;
37import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
38import org.apache.felix.dm.annotation.api.Init;
39import org.apache.felix.dm.annotation.api.Inject;
40import org.apache.felix.dm.annotation.api.LifecycleController;
41import org.apache.felix.dm.annotation.api.Registered;
Pierre De Rop1c0431f2016-02-06 23:28:44 +000042import org.apache.felix.dm.annotation.api.RepeatableProperty;
Pierre De Rop3a00a212015-03-01 09:27:46 +000043import org.apache.felix.dm.annotation.api.ResourceAdapterService;
44import org.apache.felix.dm.annotation.api.ResourceDependency;
45import org.apache.felix.dm.annotation.api.ServiceDependency;
46import org.apache.felix.dm.annotation.api.Start;
47import org.apache.felix.dm.annotation.api.Stop;
48import org.apache.felix.dm.annotation.api.Unregistered;
49import org.json.JSONArray;
50import org.json.JSONException;
51import org.json.JSONObject;
52import org.osgi.framework.Bundle;
53
54import aQute.bnd.osgi.Annotation;
55import aQute.bnd.osgi.ClassDataCollector;
56import aQute.bnd.osgi.Clazz;
57import aQute.bnd.osgi.Descriptors.TypeRef;
58import aQute.bnd.osgi.Verifier;
59
60/**
61 * This is the scanner which does all the annotation parsing on a given class.
62 * To start the parsing, just invoke the parseClassFileWithCollector and finish methods.
63 * Once parsed, the corresponding component descriptors can be built using the "writeTo" method.
64 *
65 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
66 */
67public class AnnotationCollector extends ClassDataCollector
68{
69 private final static String A_INIT = Init.class.getName();
70 private final static String A_START = Start.class.getName();
71 private final static String A_STOP = Stop.class.getName();
72 private final static String A_DESTROY = Destroy.class.getName();
73 private final static String A_COMPOSITION = Composition.class.getName();
74 private final static String A_LIFCLE_CTRL = LifecycleController.class.getName();
75
76 private final static String A_COMPONENT = Component.class.getName();
Pierre De Rop1c0431f2016-02-06 23:28:44 +000077 private final static String A_REPEATABLE_PROPERTY = RepeatableProperty.class.getName();
Pierre De Rop3a00a212015-03-01 09:27:46 +000078 private final static String A_SERVICE_DEP = ServiceDependency.class.getName();
79 private final static String A_CONFIGURATION_DEPENDENCY = ConfigurationDependency.class.getName();
80 private final static String A_BUNDLE_DEPENDENCY = BundleDependency.class.getName();
81 private final static String A_RESOURCE_DEPENDENCY = ResourceDependency.class.getName();
82 private final static String A_ASPECT_SERVICE = AspectService.class.getName();
83 private final static String A_ADAPTER_SERVICE = AdapterService.class.getName();
84 private final static String A_BUNDLE_ADAPTER_SERVICE = BundleAdapterService.class.getName();
85 private final static String A_RESOURCE_ADAPTER_SERVICE = ResourceAdapterService.class.getName();
86 private final static String A_FACTORYCONFIG_ADAPTER_SERVICE = FactoryConfigurationAdapterService.class.getName();
87 private final static String A_INJECT = Inject.class.getName();
88 private final static String A_REGISTERED = Registered.class.getName();
89 private final static String A_UNREGISTERED = Unregistered.class.getName();
90
91 private Logger m_logger;
92 private String m_className;
93 private String[] m_interfaces;
94 private boolean m_isField;
95 private String m_field;
96 private String m_method;
97 private String m_descriptor;
98 private Set<String> m_dependencyNames = new HashSet<String>();
Pierre De Rop1c0431f2016-02-06 23:28:44 +000099 private List<EntryWriter> m_writers = new ArrayList<EntryWriter>();
Pierre De Rop3a00a212015-03-01 09:27:46 +0000100 private MetaType m_metaType;
101 private String m_startMethod;
102 private String m_stopMethod;
103 private String m_initMethod;
104 private String m_destroyMethod;
105 private String m_compositionMethod;
106 private String m_starter;
107 private String m_stopper;
108 private Set<String> m_importService = new HashSet<String>();
109 private Set<String> m_exportService = new HashSet<String>();
110 private String m_bundleContextField;
111 private String m_dependencyManagerField;
112 private String m_componentField;
113 private String m_registeredMethod;
114 private String m_unregisteredMethod;
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000115 private Annotation m_repeatableProperty;
116 private final List<EntryType> m_componentTypes = Arrays.asList(EntryType.Component, EntryType.AspectService, EntryType.AdapterService,
117 EntryType.BundleAdapterService, EntryType.ResourceAdapterService, EntryType.FactoryConfigurationAdapterService);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000118
119 /**
120 * Makes a new Collector for parsing a given class.
121 * @param reporter the object used to report logs.
122 */
123 public AnnotationCollector(Logger reporter, MetaType metaType)
124 {
125 m_logger = reporter;
126 m_metaType = metaType;
127 }
128
129 /**
130 * Parses the name of the class.
131 * @param access the class access
132 * @param name the class name (package are "/" separated).
133 */
134 @Override
135 public void classBegin(int access, TypeRef name)
136 {
137 m_className = name.getFQN();
138 m_logger.debug("class name: %s", m_className);
139 }
140
141 /**
142 * Parses the implemented interfaces ("/" separated).
143 */
144 @Override
145 public void implementsInterfaces(TypeRef[] interfaces)
146 {
147 List<String> result = new ArrayList<String>();
148 for (int i = 0; i < interfaces.length; i++)
149 {
150 if (!interfaces[i].getBinary().equals("scala/ScalaObject"))
151 {
152 result.add(interfaces[i].getFQN());
153 }
154 }
155
156 m_interfaces = result.toArray(new String[result.size()]);
157 m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
158 }
159
160 /**
161 * Parses a method. Always invoked BEFORE eventual method annotation.
162 */
163 @Override
164 public void method(Clazz.MethodDef method)
165 {
166 m_logger.debug("Parsed method %s, descriptor=%s", method.getName(), method.getDescriptor());
167 m_isField = false;
168 m_method = method.getName();
169 m_descriptor = method.getDescriptor().toString();
170 }
171
172 /**
173 * Parses a field. Always invoked BEFORE eventual field annotation
174 */
175 @Override
176 public void field(Clazz.FieldDef field)
177 {
178 m_logger.debug("Parsed field %s, descriptor=%s", field.getName(), field.getDescriptor());
179 m_isField = true;
180 m_field = field.getName();
181 m_descriptor = field.getDescriptor().toString();
182 }
183
184 /**
185 * An annotation has been parsed. Always invoked AFTER the "method"/"field"/"classBegin" callbacks.
186 */
187 @Override
188 public void annotation(Annotation annotation)
189 {
190 m_logger.debug("Parsing annotation: %s", annotation.getName());
191
192 if (annotation.getName().getFQN().equals(A_COMPONENT))
193 {
194 parseComponentAnnotation(annotation);
195 }
196 else if (annotation.getName().getFQN().equals(A_ASPECT_SERVICE))
197 {
198 parseAspectService(annotation);
199 }
200 else if (annotation.getName().getFQN().equals(A_ADAPTER_SERVICE))
201 {
202 parseAdapterService(annotation);
203 }
204 else if (annotation.getName().getFQN().equals(A_BUNDLE_ADAPTER_SERVICE))
205 {
206 parseBundleAdapterService(annotation);
207 }
208 else if (annotation.getName().getFQN().equals(A_RESOURCE_ADAPTER_SERVICE))
209 {
210 parseResourceAdapterService(annotation);
211 }
212 else if (annotation.getName().getFQN().equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
213 {
214 parseFactoryConfigurationAdapterService(annotation);
215 }
216 else if (annotation.getName().getFQN().equals(A_INIT))
217 {
218 m_initMethod = m_method;
219 }
220 else if (annotation.getName().getFQN().equals(A_START))
221 {
222 m_startMethod = m_method;
223 }
224 else if (annotation.getName().getFQN().equals(A_REGISTERED))
225 {
226 m_registeredMethod = m_method;
227 }
228 else if (annotation.getName().getFQN().equals(A_STOP))
229 {
230 m_stopMethod = m_method;
231 }
232 else if (annotation.getName().getFQN().equals(A_UNREGISTERED))
233 {
234 m_unregisteredMethod = m_method;
235 }
236 else if (annotation.getName().getFQN().equals(A_DESTROY))
237 {
238 m_destroyMethod = m_method;
239 }
240 else if (annotation.getName().getFQN().equals(A_COMPOSITION))
241 {
242 Patterns.parseMethod(m_method, m_descriptor, Patterns.COMPOSITION);
243 m_compositionMethod = m_method;
244 } else if (annotation.getName().getFQN().equals(A_LIFCLE_CTRL))
245 {
246 parseLifecycleAnnotation(annotation);
247 }
248 else if (annotation.getName().getFQN().equals(A_SERVICE_DEP))
249 {
250 parseServiceDependencyAnnotation(annotation);
251 }
252 else if (annotation.getName().getFQN().equals(A_CONFIGURATION_DEPENDENCY))
253 {
254 parseConfigurationDependencyAnnotation(annotation);
255 }
256 else if (annotation.getName().getFQN().equals(A_BUNDLE_DEPENDENCY))
257 {
258 parseBundleDependencyAnnotation(annotation);
259 }
260 else if (annotation.getName().getFQN().equals(A_RESOURCE_DEPENDENCY))
261 {
262 parseRersourceDependencyAnnotation(annotation);
263 }
264 else if (annotation.getName().getFQN().equals(A_INJECT))
265 {
266 parseInject(annotation);
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000267 }
268 else if (annotation.getName().getFQN().equals(A_REPEATABLE_PROPERTY))
269 {
270 parseRepeatableProperties(annotation);
271 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000272 }
273
274 /**
275 * Finishes up the class parsing. This method must be called once the parseClassFileWithCollector method has returned.
276 * @return true if some annotations have been parsed, false if not.
277 */
278 public boolean finish()
279 {
280 if (m_writers.size() == 0)
281 {
282 m_logger.info("No components found for class " + m_className);
283 return false;
284 }
285
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000286 // We must have at least a valid component annotation type (component, aspect, or adapters)
287
288 EntryWriter componentWriter = m_writers.stream()
289 .filter(writer -> m_componentTypes.indexOf(writer.getEntryType()) != -1)
290 .findFirst()
291 .orElseThrow(() -> new IllegalStateException(": the class " + m_className + " must be annotated with either one of the following types: " + m_componentTypes));
292
293 // Add any repeated @Property annotation to the component (or to the aspect, or adapter).
294
295 if (m_repeatableProperty != null)
296 {
297 Object[] properties = m_repeatableProperty.get("value");
298 for (Object property : properties)
299 {
300 // property is actually a @Property annotation.
301 parseProperty((Annotation) property, componentWriter);
302 }
303 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000304
305 StringBuilder sb = new StringBuilder();
306 sb.append("Parsed annotation for class ");
307 sb.append(m_className);
308 for (int i = m_writers.size() - 1; i >= 0; i--)
309 {
310 sb.append("\n\t").append(m_writers.get(i).toString());
311 }
312 m_logger.info(sb.toString());
313 return true;
314 }
315
316 /**
317 * Writes the generated component descriptor in the given print writer.
318 * The first line must be the service (@Service or AspectService).
319 * @param pw the writer where the component descriptor will be written.
320 */
321 public void writeTo(PrintWriter pw)
322 {
323 // The last element our our m_writers list contains either the Service, or the AspectService descriptor.
324 for (int i = m_writers.size() - 1; i >= 0; i--)
325 {
326 pw.println(m_writers.get(i).toString());
327 }
328 }
329
330 /**
331 * Returns list of all imported services. Imported services are deduced from every
332 * @ServiceDependency annotations.
333 * @return the list of imported services, or null
334 */
335 public Set<String> getImportService()
336 {
337 return m_importService;
338 }
339
340 /**
341 * Returns list of all exported services. Imported services are deduced from every
342 * annotations which provides a service (@Component, etc ...)
343 * @return the list of exported services, or null
344 */
345 public Set<String> getExportService()
346 {
347 return m_exportService;
348 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000349
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000350 /**
351 * Parses a Property annotation that is applied on the component class.
352 * @param property the Property annotation.
353 */
354 private void parseRepeatableProperties(Annotation repeatedProperties)
355 {
356 m_repeatableProperty = repeatedProperties;
357 }
358
Pierre De Rop3a00a212015-03-01 09:27:46 +0000359 private void parseComponentAnnotation(Annotation annotation)
360 {
361 EntryWriter writer = new EntryWriter(EntryType.Component);
362 m_writers.add(writer);
363
364 // Register previously parsed annotations common to services (Init/Start/...)
365 addCommonServiceParams(writer);
366
367 // impl attribute
368 writer.put(EntryParam.impl, m_className);
369
370 // properties attribute
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000371 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000372
373 // provides attribute.
374 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
375 {
376 // no service provided: check if @Registered/@Unregistered annotation are used
377 // and raise an error if true.
378 checkRegisteredUnregisteredNotPresent();
379 }
380
381 // factorySet attribute (deprecated, replaced by factoryName)
382 String factorySetName = writer.putString(annotation, EntryParam.factorySet, null);
383 if (factorySetName != null)
384 {
385 // When a component defines a factorySet, it means that a java.util.Set will
386 // be provided into the OSGi registry, in order to let anoter component add
387 // some component instance configurations into it.
388 // So, we have to indicate that the Set is provided as a service, in the Export-Serviec
389 // header.
390 m_exportService.add("java.util.Set");
391 }
392
393 // factoryName attribute
394 String factoryName = writer.putString(annotation, EntryParam.factoryName, null);
395 if (factoryName != null)
396 {
397 // When a component defines a factoryName, it means that a ComponentFactory will
398 // be provided into the OSGi registry, in order to let another component create some component instances.
399 // So, we have to indicate that the ComponentFactory is provided as a service, in the Export-Serviec
400 // header.
401 m_exportService.add("org.apache.felix.dependencymanager.runtime.api.ComponentFactory");
402 }
403
404 // factoryConfigure attribute
405 writer.putString(annotation, EntryParam.factoryConfigure, null);
406
407 // factoryMethod attribute
408 writer.putString(annotation, EntryParam.factoryMethod, null);
409 }
410
411 private void addCommonServiceParams(EntryWriter writer)
412 {
413 if (m_initMethod != null)
414 {
415 writer.put(EntryParam.init, m_initMethod);
416 }
417
418 if (m_startMethod != null)
419 {
420 writer.put(EntryParam.start, m_startMethod);
421 }
422
423 if (m_registeredMethod != null)
424 {
425 writer.put(EntryParam.registered, m_registeredMethod);
426 }
427
428 if (m_stopMethod != null)
429 {
430 writer.put(EntryParam.stop, m_stopMethod);
431 }
432
433 if (m_unregisteredMethod != null)
434 {
435 writer.put(EntryParam.unregistered, m_unregisteredMethod);
436 }
437
438 if (m_destroyMethod != null)
439 {
440 writer.put(EntryParam.destroy, m_destroyMethod);
441 }
442
443 if (m_compositionMethod != null)
444 {
445 writer.put(EntryParam.composition, m_compositionMethod);
446 }
447
448 if (m_starter != null)
449 {
450 writer.put(EntryParam.starter, m_starter);
451 }
452
453 if (m_stopper != null)
454 {
455 writer.put(EntryParam.stopper, m_stopper);
456 if (m_starter == null)
457 {
458 throw new IllegalArgumentException("Can't use a @LifecycleController annotation for stopping a service without declaring a " +
459 "@LifecycleController that starts the component in class " + m_className);
460 }
461 }
462
463 if (m_bundleContextField != null)
464 {
465 writer.put(EntryParam.bundleContextField, m_bundleContextField);
466 }
467
468 if (m_dependencyManagerField != null)
469 {
470 writer.put(EntryParam.dependencyManagerField, m_dependencyManagerField);
471 }
472
473 if (m_componentField != null)
474 {
475 writer.put(EntryParam.componentField, m_componentField);
476 }
477 }
478
479 /**
480 * Parses a ServiceDependency Annotation.
481 * @param annotation the ServiceDependency Annotation.
482 */
483 private void parseServiceDependencyAnnotation(Annotation annotation)
484 {
485 EntryWriter writer = new EntryWriter(EntryType.ServiceDependency);
486 m_writers.add(writer);
487
488 // service attribute
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000489 String service = parseClassAttrValue(annotation.get(EntryParam.service.toString()));
490 if (service == null)
Pierre De Rop3a00a212015-03-01 09:27:46 +0000491 {
492 if (m_isField)
493 {
494 service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
495 }
496 else
497 {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000498 // parse "bind(Component, ServiceReference, Service)" signature
499 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS1, 3, false);
500
501 if (service == null) {
502 // parse "bind(Component, Service)" signature
503 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS2, 2, false);
504 }
505
506 if (service == null) {
507 // parse "bind(Component, Map, Service)" signature
508 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS3, 3, false);
509 }
510
511 if (service == null) {
512 // parse "bind(ServiceReference, Service)" signature
513 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS4, 2, false);
514 }
515
516 if (service == null) {
517 // parse "bind(Service)" signature
518 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS5, 1, false);
519 }
520
521 if (service == null) {
522 // parse "bind(Service, Map)" signature
523 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS6, 1, false);
524 }
525
526 if (service == null) {
527 // parse "bind(Map, Service)" signature
528 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS7, 2, false);
529 }
530
531 if (service == null) {
532 // parse "bind(Service, Dictionary)" signature
533 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS8, 1, false);
534 }
535
536 if (service == null) {
537 // parse "bind(Dictionary, Service)" signature
538 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS9, 2, true);
539 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000540 }
541 }
542 writer.put(EntryParam.service, service);
543
544 // Store this service in list of imported services.
545 m_importService.add(service);
546
547 // autoConfig attribute
548 if (m_isField)
549 {
550 writer.put(EntryParam.autoConfig, m_field);
551 }
552
553 // filter attribute
554 String filter = annotation.get(EntryParam.filter.toString());
555 if (filter != null)
556 {
557 Verifier.verifyFilter(filter, 0);
558 writer.put(EntryParam.filter, filter);
559 }
560
561 // defaultImpl attribute
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000562 writer.putClass(annotation, EntryParam.defaultImpl);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000563
564 // added callback
565 writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
566
567 // timeout parameter
568 writer.putString(annotation, EntryParam.timeout, null);
569 Long t = (Long) annotation.get(EntryParam.timeout.toString());
570 if (t != null && t.longValue() < -1)
571 {
572 throw new IllegalArgumentException("Invalid timeout value " + t + " in ServiceDependency annotation from class " + m_className);
573 }
574
575 // required attribute (not valid if parsing a temporal service dependency)
576 writer.putString(annotation, EntryParam.required, null);
577
578 // changed callback
579 writer.putString(annotation, EntryParam.changed, null);
580
581 // removed callback
582 writer.putString(annotation, EntryParam.removed, null);
583
584 // name attribute
585 parseDependencyName(writer, annotation);
586
587 // propagate attribute
588 writer.putString(annotation, EntryParam.propagate, null);
589 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000590
591 /**
592 * Parse the value of a given annotation attribute (which is of type 'class').
593 * This method is compatible with bndtools 2.4.1 (where the annotation.get() method returns a String of the form "Lfull/class/name;"),
594 * and with bndtools 3.x.x (where the annotation.get() method returns a TypeRef).
595 *
596 * @param annot the annotation which contains the given attribute
597 * @param attr the attribute name (of 'class' type).
598 * @return the annotation class attribute value
599 */
600 public static String parseClassAttrValue(Object value) {
601 if (value instanceof String)
602 {
603 return Patterns.parseClass((String) value, Patterns.CLASS, 1);
604 }
605 else if (value instanceof TypeRef)
606 {
607 return ((TypeRef) value).getFQN();
608 }
609 else if (value == null) {
610 return null;
611 }
612 else {
613 throw new IllegalStateException("can't parse class attribute value from " + value);
614 }
615 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000616
617 /**
618 * Parses a ConfigurationDependency annotation.
619 * @param annotation the ConfigurationDependency annotation.
620 */
621 private void parseConfigurationDependencyAnnotation(Annotation annotation)
622 {
623 EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
624 m_writers.add(writer);
625
Pierre De Rop40ecace2016-02-09 21:44:38 +0000626 // The pid is either:
627 //
628 // - the fqdn of the configuration proxy type, if the callback accepts an interface (not a Dictionary).
629 // - or the fqdn of the class specified by the pidFromClass attribute
630 // - or the value of the pid attribute
631 // - or by default the fdqn of the class where the annotation is found
632
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000633 String pidFromClass = parseClassAttrValue(annotation.get(EntryParam.pidClass.toString()));
Pierre De Rop40ecace2016-02-09 21:44:38 +0000634 String pid = pidFromClass != null ? pidFromClass : get(annotation, EntryParam.pid.toString(), null);
635
636 // Check if annotation is applied on "updated(ConfigProxyType)"
637 String confProxyType = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS5, 1, false);
638 if (confProxyType != null)
639 {
640 if (! Dictionary.class.getName().equals(confProxyType))
641 {
642 // It's a conf proxy type.
Pierre De Rop9e5cdba2016-02-17 20:35:16 +0000643 writer.put(EntryParam.configType, confProxyType);
Pierre De Rop40ecace2016-02-09 21:44:38 +0000644 }
645 else
646 {
647 confProxyType = null;
648 }
649
650 }
651 else
652 {
653 // Check if annotation is applied on "updated(Component, ConfigProxyType)"
654 confProxyType = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS2, 2, false);
Pierre De Rop40ecace2016-02-09 21:44:38 +0000655 if (! Dictionary.class.getName().equals(confProxyType))
656 {
657 // It's a conf proxy type.
Pierre De Rop9e5cdba2016-02-17 20:35:16 +0000658 writer.put(EntryParam.configType, confProxyType);
Pierre De Rop40ecace2016-02-09 21:44:38 +0000659 }
660 else
661 {
662 confProxyType = null;
663 }
664 }
665
666 if (pid == null)
667 {
668 if (confProxyType != null)
669 {
670 pid = confProxyType;
671 }
672 else
673 {
674 pid = m_className;
675 }
676 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000677
Pierre De Rop3a00a212015-03-01 09:27:46 +0000678 writer.put(EntryParam.pid, pid);
679
680 // the method on which the annotation is applied
681 writer.put(EntryParam.updated, m_method);
682
683 // propagate attribute
684 writer.putString(annotation, EntryParam.propagate, null);
685
686 // Property Meta Types
687 parseMetaTypes(annotation, pid, false);
688
689 // name attribute
690 parseDependencyName(writer, annotation);
691 }
692
693 /**
694 * Parses an AspectService annotation.
695 * @param annotation
696 */
697 private void parseAspectService(Annotation annotation)
698 {
699 EntryWriter writer = new EntryWriter(EntryType.AspectService);
700 m_writers.add(writer);
701
702 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
703 addCommonServiceParams(writer);
704
705 // Parse service filter
706 String filter = annotation.get(EntryParam.filter.toString());
707 if (filter != null)
708 {
709 Verifier.verifyFilter(filter, 0);
710 writer.put(EntryParam.filter, filter);
711 }
712
713 // Parse service aspect ranking
714 Integer ranking = annotation.get(EntryParam.ranking.toString());
715 writer.put(EntryParam.ranking, ranking.toString());
716
717 // Generate Aspect Implementation
718 writer.put(EntryParam.impl, m_className);
719
720 // Parse Aspect properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000721 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000722
723 // Parse field/added/changed/removed attributes
724 parseAspectOrAdapterCallbackMethods(annotation, writer);
725
726 // Parse service interface this aspect is applying to
727 Object service = annotation.get(EntryParam.service.toString());
728 if (service == null)
729 {
730 if (m_interfaces == null)
731 {
732 throw new IllegalStateException("Invalid AspectService annotation: " +
733 "the service attribute has not been set and the class " + m_className
734 + " does not implement any interfaces");
735 }
736 if (m_interfaces.length != 1)
737 {
738 throw new IllegalStateException("Invalid AspectService annotation: " +
739 "the service attribute has not been set and the class " + m_className
740 + " implements more than one interface");
741 }
742
743 writer.put(EntryParam.service, m_interfaces[0]);
744 }
745 else
746 {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000747 writer.putClass(annotation, EntryParam.service);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000748 }
749
750 // Parse factoryMethod attribute
751 writer.putString(annotation, EntryParam.factoryMethod, null);
752 }
753
754 private void parseAspectOrAdapterCallbackMethods(Annotation annotation, EntryWriter writer)
755 {
756 String field = annotation.get(EntryParam.field.toString());
757 String added = annotation.get(EntryParam.added.toString());
758 String changed = annotation.get(EntryParam.changed.toString());
759 String removed = annotation.get(EntryParam.removed.toString());
760 String swap = annotation.get(EntryParam.swap.toString());
761
762 // "field" and "added/changed/removed/swap" attributes can't be mixed
763 if (field != null && (added != null || changed != null || removed != null || swap != null))
764 {
765 throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
766 + " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
767 }
768
769 // Parse aspect impl field where to inject the original service.
770 writer.putString(annotation, EntryParam.field, null);
771
772 // Parse aspect impl callback methods.
773 writer.putString(annotation, EntryParam.added, null);
774 writer.putString(annotation, EntryParam.changed, null);
775 writer.putString(annotation, EntryParam.removed, null);
776 writer.putString(annotation, EntryParam.swap, null);
777 }
778
779 /**
780 * Parses an AspectService annotation.
781 * @param annotation
782 */
783 private void parseAdapterService(Annotation annotation)
784 {
785 EntryWriter writer = new EntryWriter(EntryType.AdapterService);
786 m_writers.add(writer);
787
788 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
789 addCommonServiceParams(writer);
790
791 // Generate Adapter Implementation
792 writer.put(EntryParam.impl, m_className);
793
794 // Parse adaptee filter
795 String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
796 if (adapteeFilter != null)
797 {
798 Verifier.verifyFilter(adapteeFilter, 0);
799 writer.put(EntryParam.adapteeFilter, adapteeFilter);
800 }
801
802 // Parse the mandatory adapted service interface.
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000803 writer.putClass(annotation, EntryParam.adapteeService);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000804
805 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000806 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000807
808 // Parse the provided adapter service (use directly implemented interface by default).
809 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
810 {
811 checkRegisteredUnregisteredNotPresent();
812 }
813
814 // Parse factoryMethod attribute
815 writer.putString(annotation, EntryParam.factoryMethod, null);
816
817 // Parse propagate flag.
818 // Parse factoryMethod attribute
819 writer.putString(annotation, EntryParam.propagate, null);
820
821 // Parse field/added/changed/removed attributes
822 parseAspectOrAdapterCallbackMethods(annotation, writer);
823 }
824
825 /**
826 * Parses a BundleAdapterService annotation.
827 * @param annotation
828 */
829 private void parseBundleAdapterService(Annotation annotation)
830 {
831 EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
832 m_writers.add(writer);
833
834 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
835 addCommonServiceParams(writer);
836
837 // Generate Adapter Implementation
838 writer.put(EntryParam.impl, m_className);
839
840 // Parse bundle filter
841 String filter = annotation.get(EntryParam.filter.toString());
842 if (filter != null)
843 {
844 Verifier.verifyFilter(filter, 0);
845 writer.put(EntryParam.filter, filter);
846 }
847
848 // Parse stateMask attribute
849 writer.putString(annotation, EntryParam.stateMask, Integer.valueOf(
850 Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE).toString());
851
852 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000853 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000854
855 // Parse the optional adapter service (use directly implemented interface by default).
856 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
857 {
858 checkRegisteredUnregisteredNotPresent();
859 }
860
861 // Parse propagate attribute
862 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
863
864 // Parse factoryMethod attribute
865 writer.putString(annotation, EntryParam.factoryMethod, null);
866 }
867
868 /**
869 * Parses a BundleAdapterService annotation.
870 * @param annotation
871 */
872 private void parseResourceAdapterService(Annotation annotation)
873 {
874 EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
875 m_writers.add(writer);
876
877 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
878 addCommonServiceParams(writer);
879
880 // Generate Adapter Implementation
881 writer.put(EntryParam.impl, m_className);
882
883 // Parse resource filter
884 String filter = annotation.get(EntryParam.filter.toString());
885 if (filter != null)
886 {
887 Verifier.verifyFilter(filter, 0);
888 writer.put(EntryParam.filter, filter);
889 }
890
891 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000892 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000893
894 // Parse the provided adapter service (use directly implemented interface by default).
895 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
896 {
897 checkRegisteredUnregisteredNotPresent();
898 }
899
900 // Parse propagate attribute
901 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
902
903 // Parse changed attribute
904 writer.putString(annotation, EntryParam.changed, null);
905 }
906
907 /**
908 * Parses a Factory Configuration Adapter annotation.
909 * @param annotation
910 */
911 private void parseFactoryConfigurationAdapterService(Annotation annotation)
912 {
913 EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
914 m_writers.add(writer);
915
916 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
917 addCommonServiceParams(writer);
918
919 // Generate Adapter Implementation
920 writer.put(EntryParam.impl, m_className);
921
922 // factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000923 String factoryPidClass = parseClassAttrValue(annotation.get(EntryParam.factoryPidClass.toString()));
Pierre De Rop9e5db822016-02-10 17:37:02 +0000924
Pierre De Rop9e5cdba2016-02-17 20:35:16 +0000925 // Test if a type safe configuration type is provided.
926 String configType = parseClassAttrValue(annotation.get(EntryParam.configType.toString()));
927
928 if (configType != null) {
929 writer.put(EntryParam.configType, configType);
Pierre De Rop9e5db822016-02-10 17:37:02 +0000930 }
931
Pierre De Rop9e5cdba2016-02-17 20:35:16 +0000932 String factoryPid = null;
933
934 factoryPid = get(annotation, EntryParam.factoryPid.toString(), factoryPidClass);
935 if (factoryPid == null) {
936 factoryPid = configType != null ? configType : m_className;
937 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000938
Pierre De Rop3a00a212015-03-01 09:27:46 +0000939 writer.put(EntryParam.factoryPid, factoryPid);
940
941 // Parse updated callback
942 writer.putString(annotation, EntryParam.updated, "updated");
943
944 // propagate attribute
945 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
946
947 // Parse the provided adapter service (use directly implemented interface by default).
948 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
949 {
950 checkRegisteredUnregisteredNotPresent();
951 }
952
953 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000954 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000955
956 // Parse optional meta types for configuration description.
957 parseMetaTypes(annotation, factoryPid, true);
958
959 // Parse factoryMethod attribute
960 writer.putString(annotation, EntryParam.factoryMethod, null);
961 }
962
963 private void parseBundleDependencyAnnotation(Annotation annotation)
964 {
965 EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
966 m_writers.add(writer);
967
968 String filter = annotation.get(EntryParam.filter.toString());
969 if (filter != null)
970 {
971 Verifier.verifyFilter(filter, 0);
972 writer.put(EntryParam.filter, filter);
973 }
974
975 writer.putString(annotation, EntryParam.added, m_method);
976 writer.putString(annotation, EntryParam.changed, null);
977 writer.putString(annotation, EntryParam.removed, null);
978 writer.putString(annotation, EntryParam.required, null);
979 writer.putString(annotation, EntryParam.stateMask, null);
980 writer.putString(annotation, EntryParam.propagate, null);
981 parseDependencyName(writer, annotation);
982 }
983
984 private void parseRersourceDependencyAnnotation(Annotation annotation)
985 {
986 EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
987 m_writers.add(writer);
988
989 String filter = annotation.get(EntryParam.filter.toString());
990 if (filter != null)
991 {
992 Verifier.verifyFilter(filter, 0);
993 writer.put(EntryParam.filter, filter);
994 }
995
996 if (m_isField)
997 {
998 writer.put(EntryParam.autoConfig, m_field);
999 }
1000
1001 writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
1002 writer.putString(annotation, EntryParam.changed, null);
1003 writer.putString(annotation, EntryParam.removed, null);
1004 writer.putString(annotation, EntryParam.required, null);
1005 writer.putString(annotation, EntryParam.propagate, null);
1006 writer.putString(annotation, EntryParam.factoryMethod, null);
1007 parseDependencyName(writer, annotation);
1008 }
1009
1010 /**
1011 * Parse the name of a given dependency.
1012 * @param writer The writer where to write the dependency name
1013 * @param annotation the dependency to be parsed
1014 */
1015 private void parseDependencyName(EntryWriter writer, Annotation annotation)
1016 {
1017 String name = annotation.get(EntryParam.name.toString());
1018 if (name != null)
1019 {
1020 if(! m_dependencyNames.add(name))
1021 {
1022 throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_className);
1023 }
1024 writer.put(EntryParam.name, name);
1025 }
1026 }
1027
1028 private void parseLifecycleAnnotation(Annotation annotation)
1029 {
1030 Patterns.parseField(m_field, m_descriptor, Patterns.RUNNABLE);
1031 if ("true".equals(get(annotation,EntryParam.start.name(), "true")))
1032 {
1033 if (m_starter != null) {
1034 throw new IllegalStateException("Lifecycle annotation already defined on field " +
1035 m_starter + " in class " + m_className);
1036 }
1037 m_starter = m_field;
1038 } else {
1039 if (m_stopper != null) {
1040 throw new IllegalStateException("Lifecycle annotation already defined on field " +
1041 m_stopper + " in class " + m_className);
1042 }
1043 m_stopper = m_field;
1044 }
1045 }
1046
1047 /**
1048 * Parse optional meta types annotation attributes
1049 * @param annotation
1050 */
1051 private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
1052 {
1053 if (annotation.get("metadata") != null)
1054 {
1055 String propertiesHeading = annotation.get("heading");
1056 String propertiesDesc = annotation.get("description");
1057
1058 MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
1059 for (Object p: (Object[]) annotation.get("metadata"))
1060 {
1061 Annotation property = (Annotation) p;
1062 String heading = property.get("heading");
1063 String id = property.get("id");
Pierre De Ropc40d93f2015-05-04 20:25:57 +00001064 String type = parseClassAttrValue(property.get("type"));
Pierre De Rop3a00a212015-03-01 09:27:46 +00001065 Object[] defaults = (Object[]) property.get("defaults");
1066 String description = property.get("description");
1067 Integer cardinality = property.get("cardinality");
1068 Boolean required = property.get("required");
1069
1070 MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
1071 cardinality, required);
1072
1073 Object[] optionLabels = property.get("optionLabels");
1074 Object[] optionValues = property.get("optionValues");
1075
1076 if (optionLabels == null
1077 && optionValues != null
1078 ||
1079 optionLabels != null
1080 && optionValues == null
1081 ||
1082 (optionLabels != null && optionValues != null && optionLabels.length != optionValues.length))
1083 {
1084 throw new IllegalArgumentException("invalid option labels/values specified for property "
1085 + id +
1086 " in PropertyMetadata annotation from class " + m_className);
1087 }
1088
1089 if (optionValues != null)
1090 {
1091 for (int i = 0; i < optionValues.length; i++)
1092 {
1093 ad.add(new MetaType.Option(optionValues[i].toString(), optionLabels[i].toString()));
1094 }
1095 }
1096
1097 ocd.add(ad);
1098 }
1099
1100 m_metaType.add(ocd);
1101 MetaType.Designate designate = new MetaType.Designate(pid, factory);
1102 m_metaType.add(designate);
1103 m_logger.info("Parsed MetaType Properties from class " + m_className);
1104 }
1105 }
1106
1107 /**
1108 * Parses a Property annotation (which represents a list of key-value pair).
1109 * The properties are encoded using the following json form:
1110 *
1111 * properties ::= key-value-pair*
1112 * key-value-pair ::= key value
1113 * value ::= String | String[] | value-type
1114 * value-type ::= jsonObject with value-type-info
1115 * value-type-info ::= "type"=primitive java type
1116 * "value"=String|String[]
1117 *
1118 * Exemple:
1119 *
1120 * "properties" : {
1121 * "string-param" : "string-value",
1122 * "string-array-param" : ["str1", "str2],
1123 * "long-param" : {"type":"java.lang.Long", "value":"1"}}
1124 * "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
1125 * }
1126 * }
1127 *
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001128 * @param component the component annotation which contains a "properties" attribute. The component can be either a @Component, or an aspect, or an adapter.
1129 * @param writer the object where the parsed attributes are written.
Pierre De Rop3a00a212015-03-01 09:27:46 +00001130 */
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001131 private void parseProperties(Annotation component, EntryWriter writer)
1132 {
1133 Object[] properties = component.get(EntryParam.properties.toString());
1134 if (properties != null)
1135 {
1136 for (Object property : properties)
1137 {
1138 Annotation propertyAnnotation = (Annotation) property;
1139 parseProperty(propertyAnnotation, writer);
1140 }
1141 }
1142 }
1143
1144 /**
1145 * Parses a Property annotation. The result is added to the associated writer object
1146 * @param annotation the @Property annotation.
1147 * @param writer the writer object where the parsed property will be added to.
1148 */
1149 private void parseProperty(Annotation property, EntryWriter writer)
Pierre De Rop3a00a212015-03-01 09:27:46 +00001150 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001151 EntryParam attribute = EntryParam.properties;
Pierre De Rop3a00a212015-03-01 09:27:46 +00001152 try
1153 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001154 JSONObject properties = writer.getJsonObject(attribute);
1155 if (properties == null) {
1156 properties = new JSONObject();
1157 }
1158
1159 String name = (String) property.get("name");
1160 String type = parseClassAttrValue(property.get("type"));
1161 Class<?> classType;
1162 try
Pierre De Rop3a00a212015-03-01 09:27:46 +00001163 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001164 classType = (type == null) ? String.class : Class.forName(type);
1165 }
1166 catch (ClassNotFoundException e)
1167 {
1168 // Theorically impossible
1169 throw new IllegalArgumentException("Invalid Property type " + type
1170 + " from annotation " + property + " in class " + m_className);
1171 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001172
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001173 Object[] values;
Pierre De Rop3a00a212015-03-01 09:27:46 +00001174
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001175 if ((values = property.get("value")) != null)
1176 {
1177 values = checkPropertyType(name, classType, values);
1178 addProperty(properties, name, values, classType);
1179 }
1180 else if ((values = property.get("values")) != null)
1181 { // deprecated
1182 values = checkPropertyType(name, classType, values);
1183 addProperty(properties, name, values, classType);
1184 }
1185 else if ((values = property.get("longValue")) != null)
1186 {
1187 addProperty(properties, name, values, Long.class);
1188 }
1189 else if ((values = property.get("doubleValue")) != null)
1190 {
1191 addProperty(properties, name, values, Double.class);
1192 }
1193 else if ((values = property.get("floatValue")) != null)
1194 {
1195 addProperty(properties, name, values, Float.class);
1196 }
1197 else if ((values = property.get("intValue")) != null)
1198 {
1199 addProperty(properties, name, values, Integer.class);
1200 }
1201 else if ((values = property.get("byteValue")) != null)
1202 {
1203 addProperty(properties, name, values, Byte.class);
1204 }
1205 else if ((values = property.get("charValue")) != null)
1206 {
1207 addProperty(properties, name, values, Character.class);
1208 }
1209 else if ((values = property.get("booleanValue")) != null)
1210 {
1211 addProperty(properties, name, values, Boolean.class);
1212 }
1213 else if ((values = property.get("shortValue")) != null)
1214 {
1215 addProperty(properties, name, values, Short.class);
1216 }
1217 else
1218 {
1219 throw new IllegalArgumentException(
1220 "Missing Property value from annotation " + property + " in class " + m_className);
1221 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001222
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001223 if (properties.length() > 0) {
Pierre De Rop3a00a212015-03-01 09:27:46 +00001224 writer.putJsonObject(attribute, properties);
1225 }
1226 }
1227 catch (JSONException e)
1228 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001229 throw new IllegalArgumentException("Unexpected exception while parsing Property from class " + m_className, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001230 }
1231 }
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001232
Pierre De Rop3a00a212015-03-01 09:27:46 +00001233 /**
1234 * Checks if a property contains values that are compatible with a give primitive type.
1235 *
1236 * @param name the property name
1237 * @param values the values for the property name
1238 * @param type the primitive type.
1239 * @return the same property values, possibly modified if the type is 'Character' (the strings are converted to their character integer values).
1240 */
1241 private Object[] checkPropertyType(String name, Class<?> type, Object ... values)
1242 {
1243 if (type.equals(String.class))
1244 {
1245 return values;
1246 } else if (type.equals(Long.class)) {
1247 for (Object value : values) {
1248 try {
1249 Long.valueOf(value.toString());
1250 } catch (NumberFormatException e) {
1251 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1252 + " does not contain a valid Long value (" + value.toString() + ")");
1253 }
1254 }
1255 } else if (type.equals(Double.class)) {
1256 for (Object value : values) {
1257 try {
1258 Double.valueOf(value.toString());
1259 } catch (NumberFormatException e) {
1260 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1261 + " does not contain a valid Double value (" + value.toString() + ")");
1262 }
1263 }
1264 } else if (type.equals(Float.class)) {
1265 for (Object value : values) {
1266 try {
1267 Float.valueOf(value.toString());
1268 } catch (NumberFormatException e) {
1269 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1270 + " does not contain a valid Float value (" + value.toString() + ")");
1271 }
1272 }
1273 } else if (type.equals(Integer.class)) {
1274 for (Object value : values) {
1275 try {
1276 Integer.valueOf(value.toString());
1277 } catch (NumberFormatException e) {
1278 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1279 + " does not contain a valid Integer value (" + value.toString() + ")");
1280 }
1281 }
1282 } else if (type.equals(Byte.class)) {
1283 for (Object value : values) {
1284 try {
1285 Byte.valueOf(value.toString());
1286 } catch (NumberFormatException e) {
1287 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1288 + " does not contain a valid Byte value (" + value.toString() + ")");
1289 }
1290 }
1291 } else if (type.equals(Character.class)) {
1292 for (int i = 0; i < values.length; i++)
1293 {
1294 try
1295 {
1296 // If the string is already an integer, don't modify it
1297 Integer.valueOf(values[i].toString());
1298 }
1299 catch (NumberFormatException e)
1300 {
1301 // Converter the character string to its corresponding integer code.
1302 if (values[i].toString().length() != 1)
1303 {
1304 throw new IllegalArgumentException("Property \"" + name + "\" in class "
1305 + m_className + " does not contain a valid Character value (" + values[i] + ")");
1306 }
1307 try
1308 {
1309 values[i] = Integer.valueOf(values[i].toString().charAt(0));
1310 }
1311 catch (NumberFormatException e2)
1312 {
1313 throw new IllegalArgumentException("Property \"" + name + "\" in class "
1314 + m_className + " does not contain a valid Character value (" + values[i].toString()
1315 + ")");
1316 }
1317 }
1318 }
1319 } else if (type.equals(Boolean.class)) {
1320 for (Object value : values) {
1321 if (! value.toString().equalsIgnoreCase("false") && ! value.toString().equalsIgnoreCase("true")) {
1322 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1323 + " does not contain a valid Boolean value (" + value.toString() + ")");
1324 }
1325 }
1326 } else if (type.equals(Short.class)) {
1327 for (Object value : values) {
1328 try {
1329 Short.valueOf(value.toString());
1330 } catch (NumberFormatException e) {
1331 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1332 + " does not contain a valid Short value (" + value.toString() + ")");
1333 }
1334 }
1335 }
1336
1337 return values;
1338 }
1339
1340 /**
1341 * Adds a key/value(s) pair in a properties map
1342 * @param properties the target properties map
1343 * @param name the property name
1344 * @param value the property value(s)
1345 * @param type the property type
1346 * @throws JSONException
1347 */
1348 private void addProperty(JSONObject props, String name, Object value, Class<?> type) throws JSONException {
1349 if (value.getClass().isArray())
1350 {
1351 Object[] array = (Object[]) value;
1352 if (array.length == 1)
1353 {
1354 value = array[0];
1355 }
1356 }
1357
1358 if (type.equals(String.class))
1359 {
1360 props.put(name, value.getClass().isArray() ? new JSONArray(value) : value);
1361 }
1362 else
1363 {
1364 JSONObject jsonValue = new JSONObject();
1365 jsonValue.put("type", type.getName());
1366 jsonValue.put("value", value.getClass().isArray() ? new JSONArray(value) : value);
1367 props.put(name, jsonValue);
1368 }
1369 }
1370
1371 /**
1372 * Parse Inject annotation, used to inject some special classes in some fields
1373 * (BundleContext/DependencyManager etc ...)
1374 * @param annotation the Inject annotation
1375 */
1376 private void parseInject(Annotation annotation)
1377 {
1378 if (Patterns.BUNDLE_CONTEXT.matcher(m_descriptor).matches())
1379 {
1380 m_bundleContextField = m_field;
1381 }
1382 else if (Patterns.DEPENDENCY_MANAGER.matcher(m_descriptor).matches())
1383 {
1384 m_dependencyManagerField = m_field;
1385 }
1386 else if (Patterns.COMPONENT.matcher(m_descriptor).matches())
1387 {
1388 m_componentField = m_field;
1389 }
1390 else
1391 {
1392 throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
1393 + "\" in class " + m_className);
1394 }
1395 }
1396
1397 /**
Pierre De Rop3a00a212015-03-01 09:27:46 +00001398 * This method checks if the @Registered and/or @Unregistered annotations have been defined
1399 * while they should not, because the component does not provide a service.
1400 */
1401 private void checkRegisteredUnregisteredNotPresent()
1402 {
1403 if (m_registeredMethod != null)
1404 {
1405 throw new IllegalStateException("@Registered annotation can't be used on a Component " +
1406 " which does not provide a service (class=" + m_className + ")");
1407
1408 }
1409
1410 if (m_unregisteredMethod != null)
1411 {
1412 throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
1413 " which does not provide a service (class=" + m_className + ")");
1414
1415 }
1416 }
1417
1418 /**
1419 * Get an annotation attribute, and return a default value if its not present.
1420 * @param <T> the type of the variable which is assigned to the return value of this method.
1421 * @param annotation The annotation we are parsing
1422 * @param name the attribute name to get from the annotation
1423 * @param defaultValue the default value to return if the attribute is not found in the annotation
1424 * @return the annotation attribute value, or the defaultValue if not found
1425 */
1426 @SuppressWarnings("unchecked")
1427 private <T> T get(Annotation annotation, String name, T defaultValue)
1428 {
1429 T value = (T) annotation.get(name);
1430 return value != null ? value : defaultValue;
1431 }
1432}