| /* |
| * 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.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.MethodDescription; |
| 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 { |
| |
| /** |
| * @throws SCRDescriptorException |
| * @throws SCRDescriptorFailureException |
| * @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#process(org.apache.felix.scrplugin.annotations.ScannedClass, org.apache.felix.scrplugin.description.ClassDescription) |
| */ |
| 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(new MethodDescription(m.getAnnotatedMethod())); |
| scannedClass.processed(m); |
| } else if (m.getName().equals(Deactivate.class.getName())) { |
| cd.setDeactivate(new MethodDescription(m.getAnnotatedMethod())); |
| scannedClass.processed(m); |
| } else if (m.getName().equals(Modified.class.getName())) { |
| cd.setModified(new MethodDescription(m.getAnnotatedMethod())); |
| 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)); |
| } |
| |
| // 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() |
| */ |
| 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.getClass().getModifiers()); |
| component.setAbstract(cad.getBooleanValue("componentAbstract", classIsAbstract)); |
| |
| component.setCreatePid(cad.getBooleanValue("createPid", true)); |
| |
| 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.setEnabled(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 a method description if a name is provided. |
| * |
| * @param methodName |
| * The name of the method or <code>null</code> |
| * @return A method description if the name is not null. |
| */ |
| private MethodDescription getMethodDescription(final String methodName) { |
| if (methodName != null) { |
| return new MethodDescription(methodName); |
| } |
| return null; |
| } |
| |
| /** |
| * 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))); |
| ref.setInterfaceName(ad.getStringValue("referenceInterface", (fieldAnnotation != null ? fieldAnnotation |
| .getAnnotatedField().getType().getName() : null))); |
| 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(getMethodDescription(ad.getStringValue("bind", null))); |
| ref.setUnbind(getMethodDescription(ad.getStringValue("unbind", null))); |
| ref.setUpdated(getMethodDescription(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" }; |
| |
| /** |
| * 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; |
| } |
| |
| if (values != null) { |
| prop.setType(PropertyType.valueOf(type)); |
| if (values.length == 1) { |
| prop.setValue(values[0]); |
| } else { |
| prop.setMultiValue(values); |
| } |
| } else if (fieldAnnotation != null) { |
| |
| // Detect values from field |
| final Object value = fieldAnnotation.getAnnotatedFieldValue(); |
| if (value != null) { |
| if (value.getClass().isArray()) { |
| values = new String[Array.getLength(value)]; |
| for (int i = 0; i < values.length; i++) { |
| values[i] = Array.get(value, i).toString(); |
| } |
| prop.setMultiValue(values); |
| prop.setType(PropertyType.from(Array.get(value, 0).getClass())); |
| } else { |
| prop.setType(PropertyType.from(value.getClass())); |
| prop.setValue(value.toString()); |
| } |
| } |
| } |
| |
| final String defaultName; |
| if (fieldAnnotation != null) { |
| if (values == null) { |
| defaultName = fieldAnnotation.getAnnotatedField().getName(); |
| } else { |
| final Object value = fieldAnnotation.getAnnotatedFieldValue(); |
| if (value != null) { |
| defaultName = value.toString(); |
| } else { |
| defaultName = null; |
| } |
| } |
| } else { |
| defaultName = null; |
| } |
| prop.setName(ad.getStringValue("name", defaultName)); |
| 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); |
| } |
| |
| // options |
| final ClassAnnotation[] options = (ClassAnnotation[])ad.getValue("options"); |
| if (options != null) { |
| final List<String> propertyOptions = new ArrayList<String>(); |
| for(final ClassAnnotation po : options) { |
| propertyOptions.add(po.getStringValue("name", "")); |
| propertyOptions.add(po.getStringValue("value", "")); |
| } |
| prop.setOptions(propertyOptions.toArray(new String[propertyOptions.size()])); |
| } |
| |
| describedClass.add(prop); |
| } |
| } |
| } |