blob: 9a80f158c0cd1aa38572d886beba459a3fe620f7 [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.
643 writer.put(EntryParam.confProxyType, confProxyType);
644 }
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);
655 m_logger.warn("XX:%s/%s", m_descriptor, confProxyType);
656 if (! Dictionary.class.getName().equals(confProxyType))
657 {
658 // It's a conf proxy type.
659 writer.put(EntryParam.confProxyType, confProxyType);
660 }
661 else
662 {
663 confProxyType = null;
664 }
665 }
666
667 if (pid == null)
668 {
669 if (confProxyType != null)
670 {
671 pid = confProxyType;
672 }
673 else
674 {
675 pid = m_className;
676 }
677 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000678
Pierre De Rop3a00a212015-03-01 09:27:46 +0000679 writer.put(EntryParam.pid, pid);
680
681 // the method on which the annotation is applied
682 writer.put(EntryParam.updated, m_method);
683
684 // propagate attribute
685 writer.putString(annotation, EntryParam.propagate, null);
686
687 // Property Meta Types
688 parseMetaTypes(annotation, pid, false);
689
690 // name attribute
691 parseDependencyName(writer, annotation);
692 }
693
694 /**
695 * Parses an AspectService annotation.
696 * @param annotation
697 */
698 private void parseAspectService(Annotation annotation)
699 {
700 EntryWriter writer = new EntryWriter(EntryType.AspectService);
701 m_writers.add(writer);
702
703 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
704 addCommonServiceParams(writer);
705
706 // Parse service filter
707 String filter = annotation.get(EntryParam.filter.toString());
708 if (filter != null)
709 {
710 Verifier.verifyFilter(filter, 0);
711 writer.put(EntryParam.filter, filter);
712 }
713
714 // Parse service aspect ranking
715 Integer ranking = annotation.get(EntryParam.ranking.toString());
716 writer.put(EntryParam.ranking, ranking.toString());
717
718 // Generate Aspect Implementation
719 writer.put(EntryParam.impl, m_className);
720
721 // Parse Aspect properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000722 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000723
724 // Parse field/added/changed/removed attributes
725 parseAspectOrAdapterCallbackMethods(annotation, writer);
726
727 // Parse service interface this aspect is applying to
728 Object service = annotation.get(EntryParam.service.toString());
729 if (service == null)
730 {
731 if (m_interfaces == null)
732 {
733 throw new IllegalStateException("Invalid AspectService annotation: " +
734 "the service attribute has not been set and the class " + m_className
735 + " does not implement any interfaces");
736 }
737 if (m_interfaces.length != 1)
738 {
739 throw new IllegalStateException("Invalid AspectService annotation: " +
740 "the service attribute has not been set and the class " + m_className
741 + " implements more than one interface");
742 }
743
744 writer.put(EntryParam.service, m_interfaces[0]);
745 }
746 else
747 {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000748 writer.putClass(annotation, EntryParam.service);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000749 }
750
751 // Parse factoryMethod attribute
752 writer.putString(annotation, EntryParam.factoryMethod, null);
753 }
754
755 private void parseAspectOrAdapterCallbackMethods(Annotation annotation, EntryWriter writer)
756 {
757 String field = annotation.get(EntryParam.field.toString());
758 String added = annotation.get(EntryParam.added.toString());
759 String changed = annotation.get(EntryParam.changed.toString());
760 String removed = annotation.get(EntryParam.removed.toString());
761 String swap = annotation.get(EntryParam.swap.toString());
762
763 // "field" and "added/changed/removed/swap" attributes can't be mixed
764 if (field != null && (added != null || changed != null || removed != null || swap != null))
765 {
766 throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
767 + " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
768 }
769
770 // Parse aspect impl field where to inject the original service.
771 writer.putString(annotation, EntryParam.field, null);
772
773 // Parse aspect impl callback methods.
774 writer.putString(annotation, EntryParam.added, null);
775 writer.putString(annotation, EntryParam.changed, null);
776 writer.putString(annotation, EntryParam.removed, null);
777 writer.putString(annotation, EntryParam.swap, null);
778 }
779
780 /**
781 * Parses an AspectService annotation.
782 * @param annotation
783 */
784 private void parseAdapterService(Annotation annotation)
785 {
786 EntryWriter writer = new EntryWriter(EntryType.AdapterService);
787 m_writers.add(writer);
788
789 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
790 addCommonServiceParams(writer);
791
792 // Generate Adapter Implementation
793 writer.put(EntryParam.impl, m_className);
794
795 // Parse adaptee filter
796 String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
797 if (adapteeFilter != null)
798 {
799 Verifier.verifyFilter(adapteeFilter, 0);
800 writer.put(EntryParam.adapteeFilter, adapteeFilter);
801 }
802
803 // Parse the mandatory adapted service interface.
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000804 writer.putClass(annotation, EntryParam.adapteeService);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000805
806 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000807 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000808
809 // Parse the provided adapter service (use directly implemented interface by default).
810 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
811 {
812 checkRegisteredUnregisteredNotPresent();
813 }
814
815 // Parse factoryMethod attribute
816 writer.putString(annotation, EntryParam.factoryMethod, null);
817
818 // Parse propagate flag.
819 // Parse factoryMethod attribute
820 writer.putString(annotation, EntryParam.propagate, null);
821
822 // Parse field/added/changed/removed attributes
823 parseAspectOrAdapterCallbackMethods(annotation, writer);
824 }
825
826 /**
827 * Parses a BundleAdapterService annotation.
828 * @param annotation
829 */
830 private void parseBundleAdapterService(Annotation annotation)
831 {
832 EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
833 m_writers.add(writer);
834
835 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
836 addCommonServiceParams(writer);
837
838 // Generate Adapter Implementation
839 writer.put(EntryParam.impl, m_className);
840
841 // Parse bundle filter
842 String filter = annotation.get(EntryParam.filter.toString());
843 if (filter != null)
844 {
845 Verifier.verifyFilter(filter, 0);
846 writer.put(EntryParam.filter, filter);
847 }
848
849 // Parse stateMask attribute
850 writer.putString(annotation, EntryParam.stateMask, Integer.valueOf(
851 Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE).toString());
852
853 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000854 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000855
856 // Parse the optional adapter service (use directly implemented interface by default).
857 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
858 {
859 checkRegisteredUnregisteredNotPresent();
860 }
861
862 // Parse propagate attribute
863 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
864
865 // Parse factoryMethod attribute
866 writer.putString(annotation, EntryParam.factoryMethod, null);
867 }
868
869 /**
870 * Parses a BundleAdapterService annotation.
871 * @param annotation
872 */
873 private void parseResourceAdapterService(Annotation annotation)
874 {
875 EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
876 m_writers.add(writer);
877
878 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
879 addCommonServiceParams(writer);
880
881 // Generate Adapter Implementation
882 writer.put(EntryParam.impl, m_className);
883
884 // Parse resource filter
885 String filter = annotation.get(EntryParam.filter.toString());
886 if (filter != null)
887 {
888 Verifier.verifyFilter(filter, 0);
889 writer.put(EntryParam.filter, filter);
890 }
891
892 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000893 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000894
895 // Parse the provided adapter service (use directly implemented interface by default).
896 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
897 {
898 checkRegisteredUnregisteredNotPresent();
899 }
900
901 // Parse propagate attribute
902 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
903
904 // Parse changed attribute
905 writer.putString(annotation, EntryParam.changed, null);
906 }
907
908 /**
909 * Parses a Factory Configuration Adapter annotation.
910 * @param annotation
911 */
912 private void parseFactoryConfigurationAdapterService(Annotation annotation)
913 {
914 EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
915 m_writers.add(writer);
916
917 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
918 addCommonServiceParams(writer);
919
920 // Generate Adapter Implementation
921 writer.put(EntryParam.impl, m_className);
922
923 // factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000924 String factoryPidClass = parseClassAttrValue(annotation.get(EntryParam.factoryPidClass.toString()));
925 String factoryPid = factoryPidClass != null ? factoryPidClass : get(annotation, EntryParam.factoryPid.toString(), m_className);
926
Pierre De Rop3a00a212015-03-01 09:27:46 +0000927 writer.put(EntryParam.factoryPid, factoryPid);
928
929 // Parse updated callback
930 writer.putString(annotation, EntryParam.updated, "updated");
931
932 // propagate attribute
933 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
934
935 // Parse the provided adapter service (use directly implemented interface by default).
936 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
937 {
938 checkRegisteredUnregisteredNotPresent();
939 }
940
941 // Parse Adapter properties.
Pierre De Rop1c0431f2016-02-06 23:28:44 +0000942 parseProperties(annotation, writer);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000943
944 // Parse optional meta types for configuration description.
945 parseMetaTypes(annotation, factoryPid, true);
946
947 // Parse factoryMethod attribute
948 writer.putString(annotation, EntryParam.factoryMethod, null);
949 }
950
951 private void parseBundleDependencyAnnotation(Annotation annotation)
952 {
953 EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
954 m_writers.add(writer);
955
956 String filter = annotation.get(EntryParam.filter.toString());
957 if (filter != null)
958 {
959 Verifier.verifyFilter(filter, 0);
960 writer.put(EntryParam.filter, filter);
961 }
962
963 writer.putString(annotation, EntryParam.added, m_method);
964 writer.putString(annotation, EntryParam.changed, null);
965 writer.putString(annotation, EntryParam.removed, null);
966 writer.putString(annotation, EntryParam.required, null);
967 writer.putString(annotation, EntryParam.stateMask, null);
968 writer.putString(annotation, EntryParam.propagate, null);
969 parseDependencyName(writer, annotation);
970 }
971
972 private void parseRersourceDependencyAnnotation(Annotation annotation)
973 {
974 EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
975 m_writers.add(writer);
976
977 String filter = annotation.get(EntryParam.filter.toString());
978 if (filter != null)
979 {
980 Verifier.verifyFilter(filter, 0);
981 writer.put(EntryParam.filter, filter);
982 }
983
984 if (m_isField)
985 {
986 writer.put(EntryParam.autoConfig, m_field);
987 }
988
989 writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
990 writer.putString(annotation, EntryParam.changed, null);
991 writer.putString(annotation, EntryParam.removed, null);
992 writer.putString(annotation, EntryParam.required, null);
993 writer.putString(annotation, EntryParam.propagate, null);
994 writer.putString(annotation, EntryParam.factoryMethod, null);
995 parseDependencyName(writer, annotation);
996 }
997
998 /**
999 * Parse the name of a given dependency.
1000 * @param writer The writer where to write the dependency name
1001 * @param annotation the dependency to be parsed
1002 */
1003 private void parseDependencyName(EntryWriter writer, Annotation annotation)
1004 {
1005 String name = annotation.get(EntryParam.name.toString());
1006 if (name != null)
1007 {
1008 if(! m_dependencyNames.add(name))
1009 {
1010 throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_className);
1011 }
1012 writer.put(EntryParam.name, name);
1013 }
1014 }
1015
1016 private void parseLifecycleAnnotation(Annotation annotation)
1017 {
1018 Patterns.parseField(m_field, m_descriptor, Patterns.RUNNABLE);
1019 if ("true".equals(get(annotation,EntryParam.start.name(), "true")))
1020 {
1021 if (m_starter != null) {
1022 throw new IllegalStateException("Lifecycle annotation already defined on field " +
1023 m_starter + " in class " + m_className);
1024 }
1025 m_starter = m_field;
1026 } else {
1027 if (m_stopper != null) {
1028 throw new IllegalStateException("Lifecycle annotation already defined on field " +
1029 m_stopper + " in class " + m_className);
1030 }
1031 m_stopper = m_field;
1032 }
1033 }
1034
1035 /**
1036 * Parse optional meta types annotation attributes
1037 * @param annotation
1038 */
1039 private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
1040 {
1041 if (annotation.get("metadata") != null)
1042 {
1043 String propertiesHeading = annotation.get("heading");
1044 String propertiesDesc = annotation.get("description");
1045
1046 MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
1047 for (Object p: (Object[]) annotation.get("metadata"))
1048 {
1049 Annotation property = (Annotation) p;
1050 String heading = property.get("heading");
1051 String id = property.get("id");
Pierre De Ropc40d93f2015-05-04 20:25:57 +00001052 String type = parseClassAttrValue(property.get("type"));
Pierre De Rop3a00a212015-03-01 09:27:46 +00001053 Object[] defaults = (Object[]) property.get("defaults");
1054 String description = property.get("description");
1055 Integer cardinality = property.get("cardinality");
1056 Boolean required = property.get("required");
1057
1058 MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
1059 cardinality, required);
1060
1061 Object[] optionLabels = property.get("optionLabels");
1062 Object[] optionValues = property.get("optionValues");
1063
1064 if (optionLabels == null
1065 && optionValues != null
1066 ||
1067 optionLabels != null
1068 && optionValues == null
1069 ||
1070 (optionLabels != null && optionValues != null && optionLabels.length != optionValues.length))
1071 {
1072 throw new IllegalArgumentException("invalid option labels/values specified for property "
1073 + id +
1074 " in PropertyMetadata annotation from class " + m_className);
1075 }
1076
1077 if (optionValues != null)
1078 {
1079 for (int i = 0; i < optionValues.length; i++)
1080 {
1081 ad.add(new MetaType.Option(optionValues[i].toString(), optionLabels[i].toString()));
1082 }
1083 }
1084
1085 ocd.add(ad);
1086 }
1087
1088 m_metaType.add(ocd);
1089 MetaType.Designate designate = new MetaType.Designate(pid, factory);
1090 m_metaType.add(designate);
1091 m_logger.info("Parsed MetaType Properties from class " + m_className);
1092 }
1093 }
1094
1095 /**
1096 * Parses a Property annotation (which represents a list of key-value pair).
1097 * The properties are encoded using the following json form:
1098 *
1099 * properties ::= key-value-pair*
1100 * key-value-pair ::= key value
1101 * value ::= String | String[] | value-type
1102 * value-type ::= jsonObject with value-type-info
1103 * value-type-info ::= "type"=primitive java type
1104 * "value"=String|String[]
1105 *
1106 * Exemple:
1107 *
1108 * "properties" : {
1109 * "string-param" : "string-value",
1110 * "string-array-param" : ["str1", "str2],
1111 * "long-param" : {"type":"java.lang.Long", "value":"1"}}
1112 * "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
1113 * }
1114 * }
1115 *
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001116 * @param component the component annotation which contains a "properties" attribute. The component can be either a @Component, or an aspect, or an adapter.
1117 * @param writer the object where the parsed attributes are written.
Pierre De Rop3a00a212015-03-01 09:27:46 +00001118 */
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001119 private void parseProperties(Annotation component, EntryWriter writer)
1120 {
1121 Object[] properties = component.get(EntryParam.properties.toString());
1122 if (properties != null)
1123 {
1124 for (Object property : properties)
1125 {
1126 Annotation propertyAnnotation = (Annotation) property;
1127 parseProperty(propertyAnnotation, writer);
1128 }
1129 }
1130 }
1131
1132 /**
1133 * Parses a Property annotation. The result is added to the associated writer object
1134 * @param annotation the @Property annotation.
1135 * @param writer the writer object where the parsed property will be added to.
1136 */
1137 private void parseProperty(Annotation property, EntryWriter writer)
Pierre De Rop3a00a212015-03-01 09:27:46 +00001138 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001139 EntryParam attribute = EntryParam.properties;
Pierre De Rop3a00a212015-03-01 09:27:46 +00001140 try
1141 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001142 JSONObject properties = writer.getJsonObject(attribute);
1143 if (properties == null) {
1144 properties = new JSONObject();
1145 }
1146
1147 String name = (String) property.get("name");
1148 String type = parseClassAttrValue(property.get("type"));
1149 Class<?> classType;
1150 try
Pierre De Rop3a00a212015-03-01 09:27:46 +00001151 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001152 classType = (type == null) ? String.class : Class.forName(type);
1153 }
1154 catch (ClassNotFoundException e)
1155 {
1156 // Theorically impossible
1157 throw new IllegalArgumentException("Invalid Property type " + type
1158 + " from annotation " + property + " in class " + m_className);
1159 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001160
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001161 Object[] values;
Pierre De Rop3a00a212015-03-01 09:27:46 +00001162
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001163 if ((values = property.get("value")) != null)
1164 {
1165 values = checkPropertyType(name, classType, values);
1166 addProperty(properties, name, values, classType);
1167 }
1168 else if ((values = property.get("values")) != null)
1169 { // deprecated
1170 values = checkPropertyType(name, classType, values);
1171 addProperty(properties, name, values, classType);
1172 }
1173 else if ((values = property.get("longValue")) != null)
1174 {
1175 addProperty(properties, name, values, Long.class);
1176 }
1177 else if ((values = property.get("doubleValue")) != null)
1178 {
1179 addProperty(properties, name, values, Double.class);
1180 }
1181 else if ((values = property.get("floatValue")) != null)
1182 {
1183 addProperty(properties, name, values, Float.class);
1184 }
1185 else if ((values = property.get("intValue")) != null)
1186 {
1187 addProperty(properties, name, values, Integer.class);
1188 }
1189 else if ((values = property.get("byteValue")) != null)
1190 {
1191 addProperty(properties, name, values, Byte.class);
1192 }
1193 else if ((values = property.get("charValue")) != null)
1194 {
1195 addProperty(properties, name, values, Character.class);
1196 }
1197 else if ((values = property.get("booleanValue")) != null)
1198 {
1199 addProperty(properties, name, values, Boolean.class);
1200 }
1201 else if ((values = property.get("shortValue")) != null)
1202 {
1203 addProperty(properties, name, values, Short.class);
1204 }
1205 else
1206 {
1207 throw new IllegalArgumentException(
1208 "Missing Property value from annotation " + property + " in class " + m_className);
1209 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001210
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001211 if (properties.length() > 0) {
Pierre De Rop3a00a212015-03-01 09:27:46 +00001212 writer.putJsonObject(attribute, properties);
1213 }
1214 }
1215 catch (JSONException e)
1216 {
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001217 throw new IllegalArgumentException("Unexpected exception while parsing Property from class " + m_className, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001218 }
1219 }
Pierre De Rop1c0431f2016-02-06 23:28:44 +00001220
Pierre De Rop3a00a212015-03-01 09:27:46 +00001221 /**
1222 * Checks if a property contains values that are compatible with a give primitive type.
1223 *
1224 * @param name the property name
1225 * @param values the values for the property name
1226 * @param type the primitive type.
1227 * @return the same property values, possibly modified if the type is 'Character' (the strings are converted to their character integer values).
1228 */
1229 private Object[] checkPropertyType(String name, Class<?> type, Object ... values)
1230 {
1231 if (type.equals(String.class))
1232 {
1233 return values;
1234 } else if (type.equals(Long.class)) {
1235 for (Object value : values) {
1236 try {
1237 Long.valueOf(value.toString());
1238 } catch (NumberFormatException e) {
1239 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1240 + " does not contain a valid Long value (" + value.toString() + ")");
1241 }
1242 }
1243 } else if (type.equals(Double.class)) {
1244 for (Object value : values) {
1245 try {
1246 Double.valueOf(value.toString());
1247 } catch (NumberFormatException e) {
1248 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1249 + " does not contain a valid Double value (" + value.toString() + ")");
1250 }
1251 }
1252 } else if (type.equals(Float.class)) {
1253 for (Object value : values) {
1254 try {
1255 Float.valueOf(value.toString());
1256 } catch (NumberFormatException e) {
1257 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1258 + " does not contain a valid Float value (" + value.toString() + ")");
1259 }
1260 }
1261 } else if (type.equals(Integer.class)) {
1262 for (Object value : values) {
1263 try {
1264 Integer.valueOf(value.toString());
1265 } catch (NumberFormatException e) {
1266 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1267 + " does not contain a valid Integer value (" + value.toString() + ")");
1268 }
1269 }
1270 } else if (type.equals(Byte.class)) {
1271 for (Object value : values) {
1272 try {
1273 Byte.valueOf(value.toString());
1274 } catch (NumberFormatException e) {
1275 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1276 + " does not contain a valid Byte value (" + value.toString() + ")");
1277 }
1278 }
1279 } else if (type.equals(Character.class)) {
1280 for (int i = 0; i < values.length; i++)
1281 {
1282 try
1283 {
1284 // If the string is already an integer, don't modify it
1285 Integer.valueOf(values[i].toString());
1286 }
1287 catch (NumberFormatException e)
1288 {
1289 // Converter the character string to its corresponding integer code.
1290 if (values[i].toString().length() != 1)
1291 {
1292 throw new IllegalArgumentException("Property \"" + name + "\" in class "
1293 + m_className + " does not contain a valid Character value (" + values[i] + ")");
1294 }
1295 try
1296 {
1297 values[i] = Integer.valueOf(values[i].toString().charAt(0));
1298 }
1299 catch (NumberFormatException e2)
1300 {
1301 throw new IllegalArgumentException("Property \"" + name + "\" in class "
1302 + m_className + " does not contain a valid Character value (" + values[i].toString()
1303 + ")");
1304 }
1305 }
1306 }
1307 } else if (type.equals(Boolean.class)) {
1308 for (Object value : values) {
1309 if (! value.toString().equalsIgnoreCase("false") && ! value.toString().equalsIgnoreCase("true")) {
1310 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1311 + " does not contain a valid Boolean value (" + value.toString() + ")");
1312 }
1313 }
1314 } else if (type.equals(Short.class)) {
1315 for (Object value : values) {
1316 try {
1317 Short.valueOf(value.toString());
1318 } catch (NumberFormatException e) {
1319 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1320 + " does not contain a valid Short value (" + value.toString() + ")");
1321 }
1322 }
1323 }
1324
1325 return values;
1326 }
1327
1328 /**
1329 * Adds a key/value(s) pair in a properties map
1330 * @param properties the target properties map
1331 * @param name the property name
1332 * @param value the property value(s)
1333 * @param type the property type
1334 * @throws JSONException
1335 */
1336 private void addProperty(JSONObject props, String name, Object value, Class<?> type) throws JSONException {
1337 if (value.getClass().isArray())
1338 {
1339 Object[] array = (Object[]) value;
1340 if (array.length == 1)
1341 {
1342 value = array[0];
1343 }
1344 }
1345
1346 if (type.equals(String.class))
1347 {
1348 props.put(name, value.getClass().isArray() ? new JSONArray(value) : value);
1349 }
1350 else
1351 {
1352 JSONObject jsonValue = new JSONObject();
1353 jsonValue.put("type", type.getName());
1354 jsonValue.put("value", value.getClass().isArray() ? new JSONArray(value) : value);
1355 props.put(name, jsonValue);
1356 }
1357 }
1358
1359 /**
1360 * Parse Inject annotation, used to inject some special classes in some fields
1361 * (BundleContext/DependencyManager etc ...)
1362 * @param annotation the Inject annotation
1363 */
1364 private void parseInject(Annotation annotation)
1365 {
1366 if (Patterns.BUNDLE_CONTEXT.matcher(m_descriptor).matches())
1367 {
1368 m_bundleContextField = m_field;
1369 }
1370 else if (Patterns.DEPENDENCY_MANAGER.matcher(m_descriptor).matches())
1371 {
1372 m_dependencyManagerField = m_field;
1373 }
1374 else if (Patterns.COMPONENT.matcher(m_descriptor).matches())
1375 {
1376 m_componentField = m_field;
1377 }
1378 else
1379 {
1380 throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
1381 + "\" in class " + m_className);
1382 }
1383 }
1384
1385 /**
Pierre De Rop3a00a212015-03-01 09:27:46 +00001386 * This method checks if the @Registered and/or @Unregistered annotations have been defined
1387 * while they should not, because the component does not provide a service.
1388 */
1389 private void checkRegisteredUnregisteredNotPresent()
1390 {
1391 if (m_registeredMethod != null)
1392 {
1393 throw new IllegalStateException("@Registered annotation can't be used on a Component " +
1394 " which does not provide a service (class=" + m_className + ")");
1395
1396 }
1397
1398 if (m_unregisteredMethod != null)
1399 {
1400 throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
1401 " which does not provide a service (class=" + m_className + ")");
1402
1403 }
1404 }
1405
1406 /**
1407 * Get an annotation attribute, and return a default value if its not present.
1408 * @param <T> the type of the variable which is assigned to the return value of this method.
1409 * @param annotation The annotation we are parsing
1410 * @param name the attribute name to get from the annotation
1411 * @param defaultValue the default value to return if the attribute is not found in the annotation
1412 * @return the annotation attribute value, or the defaultValue if not found
1413 */
1414 @SuppressWarnings("unchecked")
1415 private <T> T get(Annotation annotation, String name, T defaultValue)
1416 {
1417 T value = (T) annotation.get(name);
1418 return value != null ? value : defaultValue;
1419 }
1420}