blob: 28a22f344aaa2ea7e1d70269d1182db9fb0dc063 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.scrplugin.processing;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.AutoDetect;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.References;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Services;
import org.apache.felix.scrplugin.SCRDescriptorException;
import org.apache.felix.scrplugin.SCRDescriptorFailureException;
import org.apache.felix.scrplugin.SpecVersion;
import org.apache.felix.scrplugin.annotations.AnnotationProcessor;
import org.apache.felix.scrplugin.annotations.ClassAnnotation;
import org.apache.felix.scrplugin.annotations.FieldAnnotation;
import org.apache.felix.scrplugin.annotations.MethodAnnotation;
import org.apache.felix.scrplugin.annotations.ScannedAnnotation;
import org.apache.felix.scrplugin.annotations.ScannedClass;
import org.apache.felix.scrplugin.description.ClassDescription;
import org.apache.felix.scrplugin.description.ComponentConfigurationPolicy;
import org.apache.felix.scrplugin.description.ComponentDescription;
import org.apache.felix.scrplugin.description.PropertyDescription;
import org.apache.felix.scrplugin.description.PropertyType;
import org.apache.felix.scrplugin.description.PropertyUnbounded;
import org.apache.felix.scrplugin.description.ReferenceCardinality;
import org.apache.felix.scrplugin.description.ReferenceDescription;
import org.apache.felix.scrplugin.description.ReferencePolicy;
import org.apache.felix.scrplugin.description.ReferencePolicyOption;
import org.apache.felix.scrplugin.description.ReferenceStrategy;
import org.apache.felix.scrplugin.description.ServiceDescription;
/**
* This is the processor for the Apache Felix SCR annotations.
*/
public class SCRAnnotationProcessor implements AnnotationProcessor {
/**
* @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#getName()
*/
@Override
public String getName() {
return "Apache Felix SCR Annotation Processor";
}
/**
* @throws SCRDescriptorException
* @throws SCRDescriptorFailureException
* @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#process(org.apache.felix.scrplugin.annotations.ScannedClass, org.apache.felix.scrplugin.description.ClassDescription)
*/
@Override
public void process(final ScannedClass scannedClass, final ClassDescription describedClass)
throws SCRDescriptorFailureException, SCRDescriptorException {
final List<ClassAnnotation> componentTags = scannedClass.getClassAnnotations(Component.class.getName());
scannedClass.processed(componentTags);
for (final ClassAnnotation cad : componentTags) {
describedClass.add(createComponent(cad, scannedClass));
}
// search for the component descriptions and use the first one
final List<ComponentDescription> componentDescs = describedClass.getDescriptions(ComponentDescription.class);
ComponentDescription found = null;
if (!componentDescs.isEmpty()) {
found = componentDescs.get(0);
}
if (found != null) {
final ComponentDescription cd = found;
// search for methods
final List<MethodAnnotation> methodTags = scannedClass.getMethodAnnotations(null);
for (final MethodAnnotation m : methodTags) {
if (m.getName().equals(Activate.class.getName())) {
cd.setActivate(m.getAnnotatedMethod().getName());
scannedClass.processed(m);
} else if (m.getName().equals(Deactivate.class.getName())) {
cd.setDeactivate(m.getAnnotatedMethod().getName());
scannedClass.processed(m);
} else if (m.getName().equals(Modified.class.getName())) {
cd.setModified(m.getAnnotatedMethod().getName());
scannedClass.processed(m);
}
}
}
// service tags
final List<ClassAnnotation> allServiceTags = new ArrayList<ClassAnnotation>();
final List<ClassAnnotation> serviceTags = scannedClass.getClassAnnotations(Service.class.getName());
if (serviceTags.size() > 0) {
scannedClass.processed(serviceTags);
allServiceTags.addAll(serviceTags);
}
// services tags - class level
final List<ClassAnnotation> servicesTags = scannedClass.getClassAnnotations(Services.class.getName());
if (servicesTags.size() > 0) {
scannedClass.processed(servicesTags);
for (final ClassAnnotation cad : servicesTags) {
final ClassAnnotation[] values = (ClassAnnotation[]) cad.getValue("value");
if (values != null) {
allServiceTags.addAll(Arrays.asList(values));
}
}
}
if (allServiceTags.size() > 0) {
describedClass.add(createService(allServiceTags, scannedClass));
}
// references - class level
final List<ClassAnnotation> referencesClassTags = scannedClass.getClassAnnotations(References.class.getName());
scannedClass.processed(referencesClassTags);
for (final ClassAnnotation cad : referencesClassTags) {
final ClassAnnotation[] values = (ClassAnnotation[]) cad.getValue("value");
if (values != null) {
createReferences(Arrays.asList(values), describedClass);
}
}
// reference - class level
final List<ClassAnnotation> refClassTags = scannedClass.getClassAnnotations(Reference.class.getName());
scannedClass.processed(refClassTags);
createReferences(refClassTags, describedClass);
// reference - field level
final List<FieldAnnotation> refFieldTags = scannedClass.getFieldAnnotations(Reference.class.getName());
scannedClass.processed(refFieldTags);
createReferences(refFieldTags, describedClass);
// properties - class level
final List<ClassAnnotation> propsClassTags = scannedClass.getClassAnnotations(Properties.class.getName());
scannedClass.processed(propsClassTags);
for (final ClassAnnotation cad : propsClassTags) {
final ClassAnnotation[] values = (ClassAnnotation[]) cad.getValue("value");
if (values != null) {
createProperties(Arrays.asList(values), describedClass);
}
}
// property - class level
final List<ClassAnnotation> propClassTags = scannedClass.getClassAnnotations(Property.class.getName());
scannedClass.processed(propClassTags);
createProperties(propClassTags, describedClass);
// property - field level
final List<FieldAnnotation> propFieldTags = scannedClass.getFieldAnnotations(Property.class.getName());
scannedClass.processed(propFieldTags);
createProperties(propFieldTags, describedClass);
}
/**
* @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#getRanking()
*/
@Override
public int getRanking() {
return 1000;
}
/**
* Create a component description.
*
* @param cad
* The component annotation for the class.
* @param scannedClass
* The scanned class.
*/
private ComponentDescription createComponent(final ClassAnnotation cad, final ScannedClass scannedClass) {
final ComponentDescription component = new ComponentDescription(cad);
final boolean classIsAbstract = Modifier.isAbstract(scannedClass.getScannedClass().getModifiers());
component.setAbstract(cad.getBooleanValue("componentAbstract", classIsAbstract));
component.setCreatePid(false); // always set to false
component.setName(cad.getStringValue("name", scannedClass.getScannedClass().getName()));
component.setLabel(cad.getStringValue("label", null));
component.setDescription(cad.getStringValue("description", null));
component.setCreateDs(cad.getBooleanValue("ds", true));
component.setCreateMetatype(cad.getBooleanValue("metatype", false));
if (cad.getValue("enabled") != null) {
component.setEnabled(cad.getBooleanValue("enabled", true));
}
if (cad.getValue("specVersion") != null) {
component.setSpecVersion(SpecVersion.fromName(cad.getValue("specVersion").toString()));
}
component.setFactory(cad.getStringValue("factory", null));
// FELIX-593: immediate attribute does not default to true all the
// times hence we only set it if declared in the tag
if (cad.getValue("immediate") != null) {
component.setImmediate(cad.getBooleanValue("immediate", false));
}
component.setInherit(cad.getBooleanValue("inherit", true));
component.setConfigurationPolicy(ComponentConfigurationPolicy.valueOf(cad.getEnumValue("policy",
ComponentConfigurationPolicy.OPTIONAL.name())));
component.setSetMetatypeFactoryPid(cad.getBooleanValue("configurationFactory", false));
// Version 1.2
component.setConfigurationPid(cad.getStringValue("configurationPid", null));
return component;
}
/**
* Create a service description
*
* @param descs
* The service annotations.
* @param scannedClass
* The scanned class
*/
private ServiceDescription createService(final List<ClassAnnotation> descs, final ScannedClass scannedClass) {
final ServiceDescription service = new ServiceDescription(descs.get(0));
final List<String> listedInterfaces = new ArrayList<String>();
for (final ClassAnnotation d : descs) {
if (d.getBooleanValue("serviceFactory", false)) {
service.setServiceFactory(true);
}
if (d.getValue("value") != null) {
final String[] interfaces = (String[]) d.getValue("value");
for (String t : interfaces) {
listedInterfaces.add(t);
}
}
}
if (listedInterfaces.size() > 0 && !listedInterfaces.contains(AutoDetect.class.getName())) {
for (final String i : listedInterfaces) {
service.addInterface(i);
}
} else {
// auto detection
addInterfaces(service, scannedClass.getScannedClass());
}
return service;
}
/**
* Recursively add interfaces to the service.
*/
private void addInterfaces(final ServiceDescription service, final Class<?> javaClass) {
if (javaClass != null) {
final Class<?>[] interfaces = javaClass.getInterfaces();
for (final Class<?> i : interfaces) {
service.addInterface(i.getName());
// recursivly add interfaces implemented by this interface
this.addInterfaces(service, i);
}
// try super class
this.addInterfaces(service, javaClass.getSuperclass());
}
}
/**
* Create reference descriptions
*
* @param descs
* List of reference annotations.s
* @param describedClass
* The described class.
*/
private void createReferences(final List<? extends ScannedAnnotation> descs, final ClassDescription describedClass) {
for (final ScannedAnnotation ad : descs) {
final ReferenceDescription ref = new ReferenceDescription(ad);
// check for field annotation
final FieldAnnotation fieldAnnotation;
if (ad instanceof FieldAnnotation) {
fieldAnnotation = (FieldAnnotation) ad;
ref.setField(fieldAnnotation.getAnnotatedField());
} else {
fieldAnnotation = null;
}
ref.setName(ad.getStringValue("name",
(fieldAnnotation != null ? fieldAnnotation.getAnnotatedField().getName() : null)));
String defaultInterfaceName = null;
if ( fieldAnnotation != null ) {
if ( fieldAnnotation.getAnnotatedField().getType().isArray() ) {
defaultInterfaceName = fieldAnnotation.getAnnotatedField().getType().getComponentType().getName();
} else {
defaultInterfaceName = fieldAnnotation.getAnnotatedField().getType().getName();
}
}
ref.setInterfaceName(ad.getStringValue("referenceInterface", defaultInterfaceName));
ref.setTarget(ad.getStringValue("target", null));
ref.setCardinality(ReferenceCardinality.valueOf(ad.getEnumValue("cardinality",
ReferenceCardinality.MANDATORY_UNARY.name())));
ref.setPolicy(ReferencePolicy.valueOf(ad.getEnumValue("policy", ReferencePolicy.STATIC.name())));
ref.setPolicyOption(ReferencePolicyOption.valueOf(ad.getEnumValue("policyOption", ReferencePolicyOption.RELUCTANT.name())));
ref.setStrategy(ReferenceStrategy.valueOf(ad.getEnumValue("strategy", ReferenceStrategy.EVENT.name())));
ref.setBind(ad.getStringValue("bind", null));
ref.setUnbind(ad.getStringValue("unbind", null));
ref.setUpdated(ad.getStringValue("updated", null));
describedClass.add(ref);
}
}
private static final String[] PROPERTY_VALUE_PROCESSING = new String[] { "String", "value", "String", "classValue", "Long",
"longValue", "Double", "doubleValue", "Float", "floatValue", "Integer", "intValue", "Byte", "byteValue", "Char",
"charValue", "Boolean", "boolValue", "Short", "shortValue", "Password", "passwordValue" };
/**
* Create properties descriptions
*
* @throws SCRDescriptorException
* @throws SCRDescriptorFailureException
*/
private void createProperties(final List<? extends ScannedAnnotation> descs, final ClassDescription describedClass)
throws SCRDescriptorFailureException, SCRDescriptorException {
for (final ScannedAnnotation ad : descs) {
final PropertyDescription prop = new PropertyDescription(ad);
// check for field annotation
final FieldAnnotation fieldAnnotation;
if (ad instanceof FieldAnnotation) {
fieldAnnotation = (FieldAnnotation) ad;
} else {
fieldAnnotation = null;
}
// Detect values from annotation
String type = null;
String[] values = null;
int index = 0;
while (type == null && index < PROPERTY_VALUE_PROCESSING.length) {
final String propType = PROPERTY_VALUE_PROCESSING[index];
final String propName = PROPERTY_VALUE_PROCESSING[index + 1];
final Object propValue = ad.getValue(propName);
if (propValue != null && propValue.getClass().isArray()) {
type = propType;
values = new String[Array.getLength(propValue)];
for (int i = 0; i < values.length; i++) {
values[i] = Array.get(propValue, i).toString();
}
}
index += 2;
}
String name = ad.getStringValue("name", null);
if (values != null) {
prop.setType(PropertyType.valueOf(type));
if (values.length == 1) {
prop.setValue(values[0]);
} else {
prop.setMultiValue(values);
}
if ( name == null ) {
final Object value = fieldAnnotation.getAnnotatedFieldValue();
if (value != null) {
name = value.toString();
}
}
} else if (fieldAnnotation != null) {
// Detect values from field
if ( name != null ) {
final Object value = fieldAnnotation.getAnnotatedFieldValue();
if (value != null) {
if (value.getClass().isArray()) {
final String[] newValues = new String[Array.getLength(value)];
for (int i = 0; i < newValues.length; i++) {
newValues[i] = Array.get(value, i).toString();
}
prop.setMultiValue(newValues);
prop.setType(PropertyType.from(fieldAnnotation.getAnnotatedField().getType().getComponentType()));
} else {
prop.setType(PropertyType.from(value.getClass()));
prop.setValue(value.toString());
}
}
} else {
if ( Modifier.isStatic(fieldAnnotation.getAnnotatedField().getModifiers()) ) {
final Object value = fieldAnnotation.getAnnotatedFieldValue();
if (value != null) {
name = value.toString();
}
} else {
// non static, no name, no value (FELIX-4393)
name = fieldAnnotation.getAnnotatedField().getName();
final Object value = fieldAnnotation.getAnnotatedFieldValue();
if (value != null) {
if (value.getClass().isArray()) {
final String[] newValues = new String[Array.getLength(value)];
for (int i = 0; i < newValues.length; i++) {
newValues[i] = Array.get(value, i).toString();
}
prop.setMultiValue(newValues);
prop.setType(PropertyType.from(fieldAnnotation.getAnnotatedField().getType().getComponentType()));
} else {
prop.setType(PropertyType.from(value.getClass()));
prop.setValue(value.toString());
}
}
}
}
}
prop.setName(name);
prop.setLabel(ad.getStringValue("label", null));
prop.setDescription(ad.getStringValue("description", null));
// check type
if ( prop.getType() == null ) {
prop.setType(PropertyType.String);
}
// private
if ( ad.getValue("propertyPrivate") != null ) {
prop.setPrivate(ad.getBooleanValue("propertyPrivate", false));
}
// cardinality handling
final PropertyUnbounded pu = PropertyUnbounded
.valueOf(ad.getEnumValue("unbounded", PropertyUnbounded.DEFAULT.name()));
prop.setUnbounded(pu);
if (pu == PropertyUnbounded.DEFAULT) {
prop.setCardinality(ad.getIntegerValue("cardinality", 0));
if (prop.getMultiValue() != null && prop.getCardinality() == 0) {
prop.setUnbounded(PropertyUnbounded.ARRAY);
}
} else {
prop.setCardinality(0);
}
if ( prop.getValue() != null ) {
if ( prop.getUnbounded() == PropertyUnbounded.ARRAY || prop.getUnbounded() == PropertyUnbounded.VECTOR ) {
prop.setMultiValue(new String[] {prop.getValue()});
} else if ( prop.getCardinality() < -1 || prop.getCardinality() > 1 ) {
prop.setMultiValue(new String[] {prop.getValue()});
}
}
// options
final ScannedAnnotation[] options = (ScannedAnnotation[])ad.getValue("options");
if (options != null) {
final List<String> propertyOptions = new ArrayList<String>();
for(final ScannedAnnotation po : options) {
propertyOptions.add(po.getStringValue("name", ""));
propertyOptions.add(po.getStringValue("value", ""));
}
prop.setOptions(propertyOptions.toArray(new String[propertyOptions.size()]));
}
describedClass.add(prop);
}
}
}