diff --git a/.mvn/maven.config b/.mvn/maven.config
index ffe18b8db0..31f4b1ab22 100644
--- a/.mvn/maven.config
+++ b/.mvn/maven.config
@@ -1,4 +1,5 @@
-Pbuild-individual-bundles
+-Ptck
-Dcompare-version-with-baselines.skip=false
-DtrimStackTrace=false
--fail-at-end
diff --git a/ds/org.eclipse.pde.ds.annotations/.classpath b/ds/org.eclipse.pde.ds.annotations/.classpath
index 81fe078c20..3e81dc0dba 100644
--- a/ds/org.eclipse.pde.ds.annotations/.classpath
+++ b/ds/org.eclipse.pde.ds.annotations/.classpath
@@ -1,7 +1,17 @@
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ds/org.eclipse.pde.ds.annotations/META-INF/MANIFEST.MF b/ds/org.eclipse.pde.ds.annotations/META-INF/MANIFEST.MF
index 7d6e04c539..dcd0e4f0c4 100644
--- a/ds/org.eclipse.pde.ds.annotations/META-INF/MANIFEST.MF
+++ b/ds/org.eclipse.pde.ds.annotations/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.pde.ds.annotations;singleton:=true
-Bundle-Version: 1.3.100.qualifier
+Bundle-Version: 1.3.200.qualifier
Bundle-Activator: org.eclipse.pde.ds.internal.annotations.Activator
Bundle-Vendor: %Bundle-Vendor
Require-Bundle: org.eclipse.ui;bundle-version="[3.105.0,4.0.0)",
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/AnnotationVisitor.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/AnnotationVisitor.java
index ddb1ad1f4b..a42643c91a 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/AnnotationVisitor.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/AnnotationVisitor.java
@@ -35,9 +35,15 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
@@ -59,14 +65,19 @@
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
+import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jface.text.BadLocationException;
@@ -84,6 +95,7 @@
import org.eclipse.pde.internal.core.text.IDocumentObject;
import org.eclipse.pde.internal.core.text.IDocumentTextNode;
import org.eclipse.pde.internal.core.text.IModelTextChangeListener;
+import org.eclipse.pde.internal.ds.core.IDSBundleProperties;
import org.eclipse.pde.internal.ds.core.IDSComponent;
import org.eclipse.pde.internal.ds.core.IDSConstants;
import org.eclipse.pde.internal.ds.core.IDSDocumentFactory;
@@ -95,6 +107,7 @@
import org.eclipse.pde.internal.ds.core.IDSProvide;
import org.eclipse.pde.internal.ds.core.IDSReference;
import org.eclipse.pde.internal.ds.core.IDSService;
+import org.eclipse.pde.internal.ds.core.IDSSingleProperty;
import org.eclipse.pde.internal.ds.core.text.DSModel;
import org.eclipse.pde.internal.ui.util.TextUtil;
import org.eclipse.text.edits.MalformedTreeException;
@@ -106,6 +119,12 @@
@SuppressWarnings("restriction")
public class AnnotationVisitor extends ASTVisitor {
+ private static final String MAP_TYPE = Map.class.getName();
+
+ private static final String BUNDLE_CONTEXT = BundleContext.class.getName();
+
+ private static final String DEFAULT_ACTIVATE_METHOD_NAME = "activate";
+
private static final String COMPONENT_CONTEXT = "org.osgi.service.component.ComponentContext"; //$NON-NLS-1$
private static final String COMPONENT_ANNOTATION = DSAnnotationCompilationParticipant.COMPONENT_ANNOTATION;
@@ -118,6 +137,8 @@ public class AnnotationVisitor extends ASTVisitor {
private static final String REFERENCE_ANNOTATION = "org.osgi.service.component.annotations.Reference"; //$NON-NLS-1$
+ private static final String COMPONENT_PROPERTY_TYPE_ANNOTATION = "org.osgi.service.component.annotations.ComponentPropertyType";
+
private static final String DESIGNATE_ANNOTATION = "org.osgi.service.metatype.annotations.Designate"; //$NON-NLS-1$
private static final Pattern PID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*"); //$NON-NLS-1$
@@ -211,10 +232,12 @@ public boolean visit(TypeDeclaration type) {
boolean isAbstract = false;
boolean isNested = false;
boolean noDefaultConstructor = false;
+ boolean hasInjectableConstructor = false;
if ((isInterface = type.isInterface())
|| (isAbstract = Modifier.isAbstract(type.getModifiers()))
|| (isNested = (!type.isPackageMemberTypeDeclaration() && !isNestedPublicStatic(type)))
- || (noDefaultConstructor = !hasDefaultConstructor(type))) {
+ || (noDefaultConstructor = !(hasDefaultConstructor(type)
+ || (hasInjectableConstructor = hasInjectableConstructor(type, problemReporter))))) {
// interfaces, abstract types, non-static/non-public nested types, or types with no default constructor cannot be components
if (!errorLevel.isIgnore()) {
if (isInterface) {
@@ -224,7 +247,21 @@ public boolean visit(TypeDeclaration type) {
} else if (isNested) {
problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notTopLevel, type.getName().getIdentifier()), type.getName().getIdentifier());
} else if (noDefaultConstructor) {
- problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_noDefaultConstructor, type.getName().getIdentifier()), type.getName().getIdentifier());
+ if (specVersion.isEqualOrHigherThan(DSAnnotationVersion.V1_4)) {
+ problemReporter.reportProblem(annotation, null,
+ NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_compatibleConstructor,
+ type.getName().getIdentifier()),
+ type.getName().getIdentifier());
+ } else {
+ if (hasInjectableConstructor) {
+ // TODO we should add an error marker that offers a quickfix to upgrade the spec
+ // version to 1.4
+ }
+ problemReporter.reportProblem(annotation, null,
+ NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_noDefaultConstructor,
+ type.getName().getIdentifier()),
+ type.getName().getIdentifier());
+ }
} else {
problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidComponentImplementationClass, type.getName().getIdentifier()), type.getName().getIdentifier());
}
@@ -313,20 +350,6 @@ private boolean isNestedPublicStatic(AbstractTypeDeclaration type) {
return false;
}
- private boolean hasDefaultConstructor(TypeDeclaration type) {
- boolean hasConstructor = false;
- for (MethodDeclaration method : type.getMethods()) {
- if (method.isConstructor()) {
- hasConstructor = true;
- if (Modifier.isPublic(method.getModifiers()) && method.parameters().isEmpty()) {
- return true;
- }
- }
- }
-
- return !hasConstructor;
- }
-
private void processComponent(TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding) throws CoreException {
// determine component name
HashMap params = new HashMap<>();
@@ -546,42 +569,17 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
}
- String[] properties;
- if ((value = params.get("property")) instanceof Object[]) { //$NON-NLS-1$
- Object[] elements = (Object[]) value;
- ArrayList list = new ArrayList<>(elements.length);
- for (Object element : elements) {
- if (element instanceof String) {
- list.add((String) element);
- }
- }
-
- properties = list.toArray(new String[list.size()]);
- } else {
- properties = new String[0];
- }
-
- String[] propertyFiles;
- if ((value = params.get("properties")) instanceof Object[]) { //$NON-NLS-1$
- Object[] elements = (Object[]) value;
- ArrayList list = new ArrayList<>(elements.length);
- for (Object element : elements) {
- if (element instanceof String) {
- list.add((String) element);
- }
- }
+ String[] properties = collectProperties("property", params);
+ String[] factoryProperties = collectProperties("factoryProperty", params);
- propertyFiles = list.toArray(new String[list.size()]);
- validateComponentPropertyFiles(annotation, ((IType) typeBinding.getJavaElement()).getJavaProject().getProject(), propertyFiles);
- } else {
- propertyFiles = new String[0];
- }
+ String[] propertyFiles = collectPropertiesFiles("properties", typeBinding, annotation, params);
+ String[] factoryPropertyFiles = collectPropertiesFiles("factoryProperties", typeBinding, annotation, params);
String configPolicy = null;
if ((value = params.get("configurationPolicy")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding configPolicyBinding = (IVariableBinding) value;
configPolicy = DSEnums.getConfigurationPolicy(configPolicyBinding.getName());
- } else if (specVersion == DSAnnotationVersion.V1_3) {
+ } else if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
for (IAnnotationBinding typeAnnotation : typeBinding.getAnnotations()) {
if (!DESIGNATE_ANNOTATION.equals(typeAnnotation.getAnnotationType().getQualifiedName())) {
continue;
@@ -605,12 +603,71 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
DSAnnotationVersion requiredVersion = DSAnnotationVersion.V1_1;
+ // The following changes where made between 1.0 (Compendium 4.1) and 1.1
+ // (compendium 4.2) we must check these
+ // if we really want to support such old DS versions, currently we just use 1.0
+ // as lowest version because the OSGi TCKs would fail otherwise:
+ // (1) Definition of the Service-Component header now uses the definition of a
+ // header from the module layer. It also allows a wildcards to be used in the
+ // last component of the path of a header entry.
+ // FIXME if manifest uses a wildcard we can assume that 1.1 is minimum
+ // (2) SCR must follow the recommendations of Property Propagation on page
+ // 86 and not propagate properties whose names start with ’.’ to service
+ // properties.
+ // XXX this is probably something we can't assert here
+ // (3) The component description now allows for a configuration policy to
+ // control whether component configurations are activated when
+ // Configuration object are present or not.
+ // FIXME if configuration policy is used 1.1 is the minimum
+ // (4) The component description now allows the names of the activate and
+ // deactivate methods to be specified. The signatures of the activate and
+ // deactivate methods are also modified.
+ // FIXME a custom name of the methods or the "modified signatures) need to
+ // trigger minimum of 1.1
+ // (5) The signatures of the bind and unbind methods are modified.
+ // FIXME check if a "new" signature is used
+ // (6) The definition of accessible methods for activate, deactivate, bind and
+ // unbind methods is expanded to include any method accessible from the
+ // component implementation class. This allows private and package
+ // private method declared in the component implementation class to be
+ // used.
+ // FIXME if non public methods are used for bind/unbind we must require DS 1.1
+ // (7) The additional signatures and additional accessibility for the activate,
+ // deactivate, bind and unbind methods can cause problems for compo-
+ // nents written to version 1.0 of this specification. The behavior in this
+ // specification only applies to component descriptions using the v1.1.0
+ // namespace.
+ // This is something not controlled by the generator and don't needs further
+ // actions
+ // (8) The XML schema and namespace have been updated to v1.1.0. It now
+ // supports extensibility for new attributes and elements. The name
+ // attribute of the component element is now optional and the default
+ // value of this attribute is the value of the class attribute of the nested
+ // implementation element. The name attribute of the reference element
+ // is now optional and the default value of this attribute is the value of the
+ // interface attribute of the reference element. The Char type for the
+ // property element has been renamed Character to match the Java type
+ // name. The attributes configuration-policy, activate, deactivate and
+ // modified have been added to the component element.
+ // FIXME usage of 'Character' type in properties require 1.1
+ // (9) When logging error messages, SCR must use a Log Service obtained
+ // using the component’s bundle context so that the resulting Log Entry is
+ // associated with the component’s bundle.
+ // This do not affect the generator
+ // (10) Clarified that target properties are component properties that can be
+ // set
+ // wherever component properties can be set, including configurations.
+ // This do not affect the generator
+ // (11) A component configuration can now avoid being deactivated when a
+ // Configuration changes by specifying the modified attribute.
+ // FIXME a modified method should require SCR 1.1
+
String configPid = null;
if ((value = params.get("configurationPid")) instanceof String) { //$NON-NLS-1$
configPid = (String) value;
validateComponentConfigPID(annotation, configPid, -1);
requiredVersion = DSAnnotationVersion.V1_2;
- } else if (specVersion == DSAnnotationVersion.V1_3 && value instanceof Object[]) {
+ } else if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion) && value instanceof Object[]) {
Object[] configPidElems = (Object[]) value;
if (configPidElems.length > 0) {
LinkedHashSet configPids = new LinkedHashSet<>(configPidElems.length);
@@ -657,7 +714,8 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
String serviceScope = null;
- if (specVersion == DSAnnotationVersion.V1_3 && (value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)
+ && (value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding serviceScopeBinding = (IVariableBinding) value;
serviceScope = DSEnums.getServiceScope(serviceScopeBinding.getName());
if (!errorLevel.isIgnore()) {
@@ -669,7 +727,8 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
}
- if (specVersion == DSAnnotationVersion.V1_3 && serviceFactory != null && serviceScope != null && !serviceScope.equals(VALUE_SERVICE_SCOPE_DEFAULT)) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion) && serviceFactory != null && serviceScope != null
+ && !serviceScope.equals(VALUE_SERVICE_SCOPE_DEFAULT)) {
// ignore servicefactory if scope specified and not <>
if (!errorLevel.isIgnore() && !serviceFactory.equals(VALUE_SERVICE_SCOPE_BUNDLE.equals(serviceScope))) {
problemReporter.reportProblem(annotation, "servicefactory", -1, true, errorLevel, Messages.AnnotationVisitor_invalidServiceFactory_ignored); //$NON-NLS-1$
@@ -778,27 +837,9 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
HashMap referenceNames = new HashMap<>();
IDSReference[] refElements = component.getReferences();
- HashMap refMap = new HashMap<>(refElements.length);
- for (IDSReference refElement : refElements) {
- String referenceName = refElement.getReferenceName();
- if (referenceName == null) {
- String referenceBind = refElement.getXMLAttributeValue(ReferenceProcessor.ATTRIBUTE_REFERENCE_FIELD);
- if (referenceBind != null) {
- referenceName = ReferenceProcessor.getReferenceName(referenceBind);
- }
+ HashMap refMap = buildReferenceMap(refElements);
- if (referenceName == null) {
- referenceName = refElement.getReferenceBind();
- if (referenceName == null) {
- referenceName = refElement.getReferenceInterface();
- }
- }
- }
-
- refMap.put(referenceName, refElement);
- }
-
- if (annotation.isNormalAnnotation() && specVersion == DSAnnotationVersion.V1_3) {
+ if (annotation.isNormalAnnotation() && DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
for (Object annotationValue : ((NormalAnnotation) annotation).values()) {
MemberValuePair annotationMemberValuePair = (MemberValuePair) annotationValue;
if (!ATTRIBUTE_COMPONENT_REFERENCE.equals(annotationMemberValuePair.getName().getIdentifier())) {
@@ -848,12 +889,13 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
references.add(reference);
ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion, requiredVersion, errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
- requiredVersion = requiredVersion.max(referenceProcessor.processReference(reference, typeBinding, referenceAnnotation, referenceAnnotationBinding, annotationParams, referenceNames));
+ requiredVersion = requiredVersion.max(referenceProcessor.processReference(reference, typeBinding,
+ referenceAnnotation, referenceAnnotationBinding, annotationParams, referenceNames));
}
}
}
-
- if (specVersion == DSAnnotationVersion.V1_3) {
+ List activations = new ArrayList<>();
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
for (FieldDeclaration field : type.getFields()) {
for (Object modifier : field.modifiers()) {
if (!(modifier instanceof Annotation)) {
@@ -871,54 +913,84 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName();
- if (!REFERENCE_ANNOTATION.equals(annotationName)) {
- continue;
- }
+ if (REFERENCE_ANNOTATION.equals(annotationName)) {
+ HashMap annotationParams = null;
+ // TODO do we really care about all fragments??
+ for (Object fragmentElement : field.fragments()) {
+ VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentElement;
+ IVariableBinding fieldBinding = fragment.resolveBinding();
+ if (fieldBinding == null) {
+ if (debug.isDebugging()) {
+ debug.trace(String.format("Unable to resolve binding for field: %s", fragment)); //$NON-NLS-1$
+ }
+
+ continue;
+ }
- HashMap annotationParams = null;
- // TODO do we really care about all fragments??
- for (Object fragmentElement : field.fragments()) {
- VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentElement;
- IVariableBinding fieldBinding = fragment.resolveBinding();
- if (fieldBinding == null) {
- if (debug.isDebugging()) {
- debug.trace(String.format("Unable to resolve binding for field: %s", fragment)); //$NON-NLS-1$
+ if (annotationParams == null) {
+ annotationParams = new HashMap<>();
+ for (IMemberValuePairBinding pair : fieldAnnotationBinding
+ .getDeclaredMemberValuePairs()) {
+ annotationParams.put(pair.getName(), pair.getValue());
+ }
}
- continue;
- }
+ String referenceName = (String) annotationParams.get("name"); //$NON-NLS-1$
+ if (referenceName == null) {
+ referenceName = fieldBinding.getName();
+ }
- if (annotationParams == null) {
- annotationParams = new HashMap<>();
- for (IMemberValuePairBinding pair : fieldAnnotationBinding.getDeclaredMemberValuePairs()) {
- annotationParams.put(pair.getName(), pair.getValue());
+ IDSReference reference = refMap.remove(referenceName);
+ if (reference == null) {
+ reference = createReference(dsFactory);
}
- }
- String referenceName = (String) annotationParams.get("name"); //$NON-NLS-1$
- if (referenceName == null) {
- referenceName = fieldBinding.getName();
- }
+ references.add(reference);
- IDSReference reference = refMap.remove(referenceName);
- if (reference == null) {
- reference = createReference(dsFactory);
+ ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion,
+ requiredVersion, errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
+ DSAnnotationVersion impliedVersion = referenceProcessor.processReference(reference, field,
+ field.getModifiers(), fieldBinding,
+ fieldAnnotation, fieldAnnotationBinding, annotationParams, referenceNames);
+ requiredVersion = impliedVersion.max(requiredVersion);
+ }
+ } else if (ACTIVATE_ANNOTATION.equals(annotationName)) {
+ for (Object fragmentElement : field.fragments()) {
+ VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentElement;
+ IVariableBinding fieldBinding = fragment.resolveBinding();
+ if (fieldBinding == null) {
+ if (debug.isDebugging()) {
+ debug.trace(String.format("Unable to resolve binding for field: %s", fragment)); //$NON-NLS-1$
+ }
+ continue;
+ }
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ String fieldName = fieldBinding.getName();
+ ITypeBinding binding = field.getType().resolveBinding();
+ // Check if activation object and add to fields...
+ if (isActivationObject(binding)) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ problemReporter.reportProblem(fieldAnnotation, null,
+ Messages.AnnotationProcessor_invalidActivate_staticField);
+ } else {
+ activations.add(new ComponentActivationAnnotation(fieldName, fieldAnnotation,
+ null, binding));
+ }
+ } else {
+ problemReporter.reportProblem(fieldAnnotation, null,
+ Messages.AnnotationProcessor_invalidActivateField, fieldName);
+ }
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ } else {
+ problemReporter.reportProblem(fieldAnnotation, null,
+ Messages.AnnotationProcessor_invalidActivate);
+ }
}
-
- references.add(reference);
-
- ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion, requiredVersion, errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
- referenceProcessor.processReference(reference, field, fieldBinding, fieldAnnotation, fieldAnnotationBinding, annotationParams, referenceNames);
- requiredVersion = DSAnnotationVersion.V1_3;
}
}
}
}
- String activate = null;
- boolean lookedForActivateMethod = false;
- IMethodBinding activateMethod = null;
- Annotation activateAnnotation = null;
String deactivate = null;
boolean lookedForDeactivateMethod = false;
IMethodBinding deactivateMethod = null;
@@ -926,6 +998,8 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
String modified = null;
IMethodBinding modifiedMethod = null;
Annotation modifiedAnnotation = null;
+
+
for (MethodDeclaration method : type.getMethods()) {
for (Object modifier : method.modifiers()) {
@@ -946,29 +1020,29 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName();
if (ACTIVATE_ANNOTATION.equals(annotationName)) {
- if (activate == null) {
- activate = method.getName().getIdentifier();
- if (specVersion == DSAnnotationVersion.V1_3) {
- activateMethod = method.resolveBinding();
- }
-
- activateAnnotation = methodAnnotation;
- validateLifeCycleMethod(methodAnnotation, "activate", method); //$NON-NLS-1$
- } else if (!errorLevel.isIgnore()) {
- problemReporter.reportProblem(methodAnnotation, null, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier());
- if (activateAnnotation != null) {
- problemReporter.reportProblem(activateAnnotation, null, Messages.AnnotationProcessor_duplicateActivateMethod, activate);
- activateAnnotation = null;
- }
+ ComponentActivationAnnotation activation;
+ String activate = method.getName().getIdentifier();
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
+ activation = new ComponentActivationAnnotation(activate, methodAnnotation, method,
+ method.resolveBinding());
+ } else {
+ // prior to 1.3 only an 'activate' method is allowed
+ activation = new ComponentActivationAnnotation(activate, methodAnnotation, null,
+ findLifeCycleMethod(typeBinding, DEFAULT_ACTIVATE_METHOD_NAME));
+ }
+ activations.add(activation);
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion) && method.isConstructor()) {
+ // will validate later...
+ } else {
+ validateLifeCycleMethod(methodAnnotation, DEFAULT_ACTIVATE_METHOD_NAME, method); // $NON-NLS-1$
}
-
continue;
}
if (DEACTIVATE_ANNOTATION.equals(annotationName)) {
if (deactivate == null) {
deactivate = method.getName().getIdentifier();
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
deactivateMethod = method.resolveBinding();
}
@@ -988,7 +1062,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
if (MODIFIED_ANNOTATION.equals(annotationName)) {
if (modified == null) {
modified = method.getName().getIdentifier();
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
modifiedMethod = method.resolveBinding();
}
@@ -1035,15 +1109,93 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
}
- if (activate == null) {
- // only remove activate="activate" if method not found
- if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$
- || ((lookedForActivateMethod = true)
- && (activateMethod = findLifeCycleMethod(typeBinding, "activate")) == null)) { //$NON-NLS-1$
- removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null);
+ if (activations.isEmpty()) {
+ // lets see if we can find one...
+ IMethodBinding binding = findLifeCycleMethod(typeBinding, DEFAULT_ACTIVATE_METHOD_NAME);
+ if (binding != null) {
+ ComponentActivationAnnotation activation = new ComponentActivationAnnotation(
+ DEFAULT_ACTIVATE_METHOD_NAME, null, null, binding);
+ activations.add(activation);
}
+ }
+ ComponentActivationAnnotation activateMethod = validateOnlyOne( activations.stream().filter(ca -> ca.isMethod()).toList());
+ ComponentActivationAnnotation activateConstructor = validateOnlyOne(
+ activations.stream().filter(ca -> ca.isConstructor()).toList());
+ // The fields are processed in lexicographical order, using String.compareTo, of
+ // the field names
+ List activateFields = activations.stream().filter(ca -> ca.isType())
+ .sorted(Comparator.comparing(ComponentActivationAnnotation::activate)).toList();
+ if (activateMethod == null || DEFAULT_ACTIVATE_METHOD_NAME.equals(activateMethod.activate())) {
+ removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null);
} else {
- component.setActivateMethod(activate);
+ component.setActivateMethod(activateMethod.activate());
+ }
+ if (activateConstructor == null || activateConstructor.parameterCount() == 0) {
+ removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_INIT, null);
+ } else {
+ component.setXMLAttribute(IDSConstants.ATTRIBUTE_COMPONENT_INIT,
+ Integer.toString(activateConstructor.parameterCount()));
+ }
+ if (activateFields.isEmpty()) {
+ removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATION_FIELDS, null);
+ } else {
+ component.setXMLAttribute(IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATION_FIELDS, activateFields.stream()
+ .map(ComponentActivationAnnotation::activate).collect(Collectors.joining(" ")));
+ }
+
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion) && activateConstructor != null) {
+ MethodDeclaration method = activateConstructor.method();
+ @SuppressWarnings("unchecked")
+ List parameters = method.parameters();
+ for (int i = 0; i < parameters.size(); i++) {
+ SingleVariableDeclaration parameter = parameters.get(i);
+ IVariableBinding variableBinding = parameter.resolveBinding();
+ Optional referenceAnnotation = annotations(parameter.modifiers())
+ .filter(a -> isReferenceAnnotation(a.resolveAnnotationBinding())).findFirst();
+ if (referenceAnnotation.isEmpty()) {
+ if (isActivationObject(variableBinding.getType())) {
+ // That is okay!
+ continue;
+ } else {
+ // the spec requires @Reference annotation on non activation objects!
+ problemReporter.reportProblem(activateConstructor.annotation(), null,
+ Messages.AnnotationProcessor_invalidConstructorArgument, parameter.getName().toString(),
+ Integer.toString(i));
+ }
+ } else {
+ Annotation constructorParameterAnnotation = referenceAnnotation.get();
+ IAnnotationBinding constructorParameterAnnotationBinding = constructorParameterAnnotation
+ .resolveAnnotationBinding();
+ if (constructorParameterAnnotationBinding == null) {
+ if (debug.isDebugging()) {
+ debug.trace(String.format("Unable to resolve binding for parameter: %s", parameter)); //$NON-NLS-1$
+ }
+ continue;
+ }
+ Map annotationParams = new HashMap<>();
+ for (IMemberValuePairBinding pair : constructorParameterAnnotationBinding
+ .getDeclaredMemberValuePairs()) {
+ annotationParams.put(pair.getName(), pair.getValue());
+ }
+ String referenceName = (String) annotationParams.get("name"); //$NON-NLS-1$
+ if (referenceName == null) {
+ referenceName = variableBinding.getName();
+ }
+ IDSReference reference = refMap.remove(referenceName);
+ if (reference == null) {
+ reference = createReference(dsFactory);
+ }
+ references.add(reference);
+ ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion, requiredVersion,
+ errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
+ referenceProcessor.processReference(reference, parameter, parameter.getModifiers(), variableBinding,
+ constructorParameterAnnotation, constructorParameterAnnotationBinding, annotationParams,
+ referenceNames);
+ reference.setXMLAttribute("parameter", Integer.toString(i));
+ removeAttribute(reference, ReferenceProcessor.ATTRIBUTE_REFERENCE_FIELD, null);
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ }
+ }
}
if (deactivate == null) {
@@ -1064,42 +1216,190 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
LinkedHashMap newPropMap = new LinkedHashMap<>();
-
- if (specVersion == DSAnnotationVersion.V1_3) {
+ // see 112.8.3 Ordering of Generated Component Properties ...
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
// collect component property types from activate, modified, and deactivate methods
- if (activateMethod == null && !lookedForActivateMethod) {
- activateMethod = findLifeCycleMethod(typeBinding, "activate"); //$NON-NLS-1$
- }
-
- if (deactivateMethod == null && !lookedForDeactivateMethod) {
- deactivateMethod = findLifeCycleMethod(typeBinding, "deactivate"); //$NON-NLS-1$
- }
-
HashSet cptClosure = new HashSet<>();
+ // 1. Properties defined through component property types used as the type of an
+ // activation object.
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ if (activateConstructor != null) {
+ // 1 a) The component property types used as parameters to the constructor.
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ collectProperties(activateConstructor.binding(), dsFactory, newPropMap, cptClosure);
+ }
+ // 1 b) The component property types used as activation fields.
+ for (ComponentActivationAnnotation activateField : activateFields) {
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ collectProperties(activateField.binding(), dsFactory, newPropMap, cptClosure);
+ }
+ }
+ // 1 c) The component property types used as parameters to the activate method.
if (activateMethod != null) {
- collectProperties(activateMethod, dsFactory, newPropMap, cptClosure);
+ collectProperties(activateMethod.binding(), dsFactory, newPropMap, cptClosure);
}
-
+ // 1 d) The component property types used as parameters to the modified method.
if (modifiedMethod != null) {
collectProperties(modifiedMethod, dsFactory, newPropMap, cptClosure);
}
-
+ // 1 e) The component property types used as parameters to the deactivate method
+ if (deactivateMethod == null && !lookedForDeactivateMethod) {
+ deactivateMethod = findLifeCycleMethod(typeBinding, "deactivate"); //$NON-NLS-1$
+ }
if (deactivateMethod != null) {
collectProperties(deactivateMethod, dsFactory, newPropMap, cptClosure);
}
-
if (!cptClosure.isEmpty()) {
- requiredVersion = DSAnnotationVersion.V1_3;
+ requiredVersion = DSAnnotationVersion.V1_3.max(requiredVersion);
+ }
+ // 2) Properties defined through component property types annotating the
+ // component implementation class.
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ List propertyTypeAnnotations = annotations(type.modifiers())
+ .filter(a -> isComponentPropertyType(a.resolveTypeBinding())).toList();
+ for (Annotation propertyType : propertyTypeAnnotations) {
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ collectComponentPropertyTypes(dsFactory, newPropMap, propertyType);
+ }
}
}
+ // 3) property element of the Component annotation.
+ updateProperties(model, type, annotation, value, properties, component, dsFactory::createProperty,
+ component.getPropertyElements(), newPropMap);
+ updateProperties(model, type, annotation, value, factoryProperties, component, dsFactory::createFactoryProperty,
+ component.getFactoryPropertyElements(), new LinkedHashMap<>());
+ // 4) properties element of the Component annotation.
+ updatePropertyFiles(propertyFiles, component, dsFactory::createProperties, component.getPropertiesElements());
+ updatePropertyFiles(factoryPropertyFiles, component, dsFactory::createFactoryProperties,
+ component.getFactoryPropertiesElements());
+ if (factoryPropertyFiles.length > 0 || factoryProperties.length > 0) {
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ }
- IDSProperty[] propElements = component.getPropertyElements();
+ if (references.isEmpty()) {
+ removeChildren(component, Arrays.asList(refElements));
+ } else {
+ // references must be declared in ascending lexicographical order of their names
+ Collections.sort(references, REF_NAME_COMPARATOR);
+
+ int firstPos;
+ if (refElements.length == 0) {
+ // insert first reference element after service element, or (if not present) last property or properties
+ service = component.getService();
+ if (service == null) {
+ firstPos = Math.max(0, indexOfLastPropertyOrProperties(component));
+ } else {
+ firstPos = component.indexOf(service) + 1;
+ }
+ } else {
+ firstPos = component.indexOf(refElements[0]);
+ }
+
+ removeChildren(component, refMap.values());
+
+ addOrMoveChildren(component, references, firstPos);
+ }
+
+ IDSImplementation impl = component.getImplementation();
+ if (impl == null) {
+ impl = dsFactory.createImplementation();
+ component.setImplementation(impl);
+ }
+
+ impl.setClassName(implClass);
+
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ if (activateConstructor != null) {
+ requiredVersion = DSAnnotationVersion.V1_4.max(requiredVersion);
+ }
+ // TODO using logger component also requires 1.4!
+ }
+
+ String xmlns = requiredVersion.getNamespace();
+ if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$
+ xmlns = (String) value;
+ validateComponentXMLNS(annotation, xmlns, requiredVersion);
+ }
+ component.setNamespace(xmlns);
+ }
+
+ private void collectComponentPropertyTypes(IDSDocumentFactory dsFactory,
+ LinkedHashMap newPropMap, Annotation propertyType) {
+ String fqdn = propertyType.getTypeName().getFullyQualifiedName();
+ ITypeBinding propertyTypeBinding = propertyType.resolveTypeBinding();
+ String prefix = getPrefix(propertyTypeBinding);
+ IMethodBinding[] methods = propertyTypeBinding.getDeclaredMethods();
+ Map map = Arrays.stream(methods)
+ .map(methodBinding -> createProperty(methodBinding, prefix, dsFactory))
+ .filter(Objects::nonNull).collect(Collectors.toMap(IDSProperty::getName,
+ Function.identity(), (a, b) -> a, LinkedHashMap::new));
+ if (propertyType instanceof NormalAnnotation normal) {
+ @SuppressWarnings("unchecked")
+ List values = normal.values();
+ for (MemberValuePair pair : values) {
+ String propName = pair.getName().getFullyQualifiedName();
+ String propValue = getExpressionValue(pair.getValue());
+ String key = NameGenerator.createPropertyName(propName, prefix, specVersion);
+ map.get(key).setPropertyValue(propValue);
+ }
+ }
+ if (propertyType instanceof MarkerAnnotation marker && map.isEmpty()) {
+ IDSProperty property = dsFactory.createProperty();
+ property.setPropertyName(NameGenerator.createClassPropertyName(fqdn, prefix));
+ property.setPropertyType(IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN);
+ property.setPropertyValue(String.valueOf(Boolean.TRUE));
+ newPropMap.remove(property.getName()); // force re-insert (append)
+ newPropMap.put(property.getName(), property);
+ return;
+ }
+ if (propertyType instanceof SingleMemberAnnotation single && map.size() == 1) {
+ IDSProperty property = dsFactory.createProperty();
+ property.setPropertyName(NameGenerator.createClassPropertyName(fqdn, prefix));
+ Expression expression = single.getValue();
+ property.setPropertyType(getPropertyType(expression.resolveTypeBinding()));
+ property.setPropertyValue(getExpressionValue(expression));
+ newPropMap.remove(property.getName()); // force re-insert (append)
+ newPropMap.put(property.getName(), property);
+ return;
+ }
+ for (IDSProperty prop : map.values()) {
+ newPropMap.remove(prop.getName()); // force re-insert (append)
+ newPropMap.put(prop.getName(), prop);
+ }
+ }
+
+ private String getExpressionValue(Expression expression) {
+ if (expression instanceof StringLiteral string) {
+ return string.getLiteralValue();
+ }
+ return expression.toString();
+ }
+
+ private String getPrefix(ITypeBinding typeBinding) {
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ IVariableBinding[] fields = typeBinding.getDeclaredFields();
+ for (IVariableBinding binding : fields) {
+ String name = binding.getName();
+ if ("PREFIX_".equals(name)) {
+ if (binding.getConstantValue() instanceof String prefix) {
+ return prefix;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private void updateProperties(IDSModel model, TypeDeclaration type,
+ Annotation annotation, Object value,
+ String[] properties, IDSComponent component, Supplier factory, T[] propElements,
+ LinkedHashMap newPropMap) {
if (newPropMap.isEmpty() && properties.length == 0) {
removeChildren(component, Arrays.asList(propElements));
} else {
// build up new property elements
- LinkedHashMap map = new LinkedHashMap<>(properties.length);
+ LinkedHashMap map = new LinkedHashMap<>(properties.length);
for (int i = 0; i < properties.length; ++i) {
String propertyStr = properties[i];
String[] pair = propertyStr.split("=", 2); //$NON-NLS-1$
@@ -1114,11 +1414,24 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
String propertyValue = pair.length > 1 ? pair[1].trim() : null;
+ if (propertyValue != null && IDSConstants.VALUE_PROPERTY_TYPE_CHAR.equals(propertyType)) {
+ // according to the spec a char must be encoded as its unicode point: For
+ // Character types, the conversion must be handled by Integer.valueOf method, a
+ // Character is always represented by its Unicode value.
+ if (propertyValue.length() == 0 || propertyValue.length() > 1) {
+ problemReporter.reportProblem(annotation, "property", i, //$NON-NLS-1$
+ NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyValue, type, value),
+ String.valueOf(value));
+ } else {
+ char c = propertyValue.charAt(0);
+ propertyValue = Integer.toString(c);
+ }
+ }
- IDSProperty property = map.get(propertyName);
+ T property = map.get(propertyName);
if (property == null) {
// create a new property
- property = dsFactory.createProperty();
+ property = factory.get();
map.put(propertyName, property);
property.setPropertyName(propertyName);
if (propertyType == null) {
@@ -1155,18 +1468,22 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
}
// reconcile against existing property elements
- HashMap propMap = new HashMap<>(propElements.length);
- for (IDSProperty propElement : propElements) {
- propMap.put(propElement.getPropertyName(), propElement);
+ HashMap propMap = new HashMap<>(propElements.length);
+ for (T propElement : propElements) {
+ T put = propMap.put(propElement.getPropertyName(), propElement);
+ if (put != null) {
+ // duplicate entry
+ removeChildren(component, List.of(put));
+ }
}
newPropMap.keySet().removeAll(map.keySet()); // force re-insert (append)
newPropMap.putAll(map);
- ArrayList propList = new ArrayList<>(newPropMap.values());
- for (ListIterator i = propList.listIterator(); i.hasNext();) {
- IDSProperty newProperty = i.next();
- IDSProperty property = propMap.remove(newProperty.getPropertyName());
+ ArrayList propList = new ArrayList<>(newPropMap.values());
+ for (ListIterator i = propList.listIterator(); i.hasNext();) {
+ T newProperty = i.next();
+ T property = propMap.remove(newProperty.getPropertyName());
if (property == null) {
continue;
}
@@ -1205,32 +1522,56 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
addOrMoveChildren(component, propList, firstPos);
}
+ }
- IDSProperties[] propFileElements = component.getPropertiesElements();
+ private String[] collectProperties(String key, Map params) {
+ Object value = params.get(key);
+ String[] properties;
+ if (value instanceof Object[]) { // $NON-NLS-1$
+ Object[] elements = (Object[]) value;
+ ArrayList list = new ArrayList<>(elements.length);
+ for (Object element : elements) {
+ if (element instanceof String) {
+ list.add((String) element);
+ }
+ }
+
+ properties = list.toArray(new String[list.size()]);
+ } else {
+ properties = new String[0];
+ }
+ return properties;
+ }
+
+ private void updatePropertyFiles(String[] propertyFiles, IDSComponent component,
+ Supplier factory, T[] propFileElements) {
if (propertyFiles.length == 0) {
removeChildren(component, Arrays.asList(propFileElements));
} else {
- HashMap propFileMap = new HashMap<>(propFileElements.length);
- for (IDSProperties propFileElement : propFileElements) {
- propFileMap.put(propFileElement.getEntry(), propFileElement);
+ HashMap propFileMap = new HashMap<>(propFileElements.length);
+ for (T propFileElement : propFileElements) {
+ T put = propFileMap.put(propFileElement.getEntry(), propFileElement);
+ if (put != null) {
+ // duplicate entry!
+ removeChildren(component, List.of(put));
+ }
}
- ArrayList propFileList = new ArrayList<>(propertyFiles.length);
+ ArrayList propFileList = new ArrayList<>(propertyFiles.length);
for (String propertyFile : propertyFiles) {
- IDSProperties propertiesElement = propFileMap.remove(propertyFile);
+ T propertiesElement = propFileMap.remove(propertyFile);
if (propertiesElement == null) {
- propertiesElement = dsFactory.createProperties();
+ propertiesElement = factory.get();
propertiesElement.setInTheModel(false); // note: workaround for PDE bug
propertiesElement.setEntry(propertyFile);
}
-
propFileList.add(propertiesElement);
}
int firstPos;
if (propFileElements.length == 0) {
// insert first properties element after last property or (if none) first child of component
- propElements = component.getPropertyElements();
+ IDSProperty[] propElements = component.getPropertyElements();
firstPos = propElements.length == 0 ? 0 : component.indexOf(propElements[propElements.length - 1]) + 1;
} else {
firstPos = component.indexOf(propFileElements[0]);
@@ -1240,46 +1581,65 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding
addOrMoveChildren(component, propFileList, firstPos);
}
+ }
- if (references.isEmpty()) {
- removeChildren(component, Arrays.asList(refElements));
- } else {
- // references must be declared in ascending lexicographical order of their names
- Collections.sort(references, REF_NAME_COMPARATOR);
-
- int firstPos;
- if (refElements.length == 0) {
- // insert first reference element after service element, or (if not present) last property or properties
- service = component.getService();
- if (service == null) {
- firstPos = Math.max(0, indexOfLastPropertyOrProperties(component));
- } else {
- firstPos = component.indexOf(service) + 1;
+ private String[] collectPropertiesFiles(String key, ITypeBinding typeBinding, Annotation annotation,
+ Map params) {
+ Object value = params.get(key);
+ String[] propertyFiles;
+ if (value instanceof Object[]) { // $NON-NLS-1$
+ Object[] elements = (Object[]) value;
+ ArrayList list = new ArrayList<>(elements.length);
+ for (Object element : elements) {
+ if (element instanceof String) {
+ list.add((String) element);
}
- } else {
- firstPos = component.indexOf(refElements[0]);
}
- removeChildren(component, refMap.values());
-
- addOrMoveChildren(component, references, firstPos);
+ propertyFiles = list.toArray(new String[list.size()]);
+ validateComponentPropertyFiles(key, annotation,
+ ((IType) typeBinding.getJavaElement()).getJavaProject().getProject(), propertyFiles);
+ } else {
+ propertyFiles = new String[0];
}
+ return propertyFiles;
+ }
- IDSImplementation impl = component.getImplementation();
- if (impl == null) {
- impl = dsFactory.createImplementation();
- component.setImplementation(impl);
- }
+ private HashMap buildReferenceMap(IDSReference[] refElements) {
+ HashMap refMap = new HashMap<>(refElements.length);
+ for (IDSReference refElement : refElements) {
+ String referenceName = refElement.getReferenceName();
+ if (referenceName == null) {
+ String referenceBind = refElement.getXMLAttributeValue(ReferenceProcessor.ATTRIBUTE_REFERENCE_FIELD);
+ if (referenceBind != null) {
+ referenceName = ReferenceProcessor.getReferenceName(referenceBind);
+ }
- impl.setClassName(implClass);
+ if (referenceName == null) {
+ referenceName = refElement.getReferenceBind();
+ if (referenceName == null) {
+ referenceName = refElement.getReferenceInterface();
+ }
+ }
+ }
- String xmlns = requiredVersion.getNamespace();
- if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$
- xmlns = (String) value;
- validateComponentXMLNS(annotation, xmlns, requiredVersion);
+ refMap.put(referenceName, refElement);
}
+ return refMap;
+ }
- component.setNamespace(xmlns);
+ private ComponentActivationAnnotation validateOnlyOne(List list) {
+ if (list.isEmpty()) {
+ return null;
+ }
+ if (list.size() == 1 || errorLevel.isIgnore()) {
+ return list.get(0);
+ }
+ for (ComponentActivationAnnotation a : list) {
+ problemReporter.reportProblem(a.annotation(), null, Messages.AnnotationProcessor_duplicateActivateMethod,
+ a.activate());
+ }
+ return null;
}
private IDSReference createReference(IDSDocumentFactory dsFactory) {
@@ -1439,83 +1799,107 @@ private String normalizePropertyElemBody(String content) {
return buf.toString();
}
- private void collectProperties(IMethodBinding method, IDSDocumentFactory factory, Map properties, Collection visited) {
- for (ITypeBinding paramTypeBinding : method.getParameterTypes()) {
+ private DSAnnotationVersion collectProperties(IBinding binding, IDSDocumentFactory factory,
+ Map properties,
+ Collection visited) {
+ DSAnnotationVersion version = DSAnnotationVersion.V1_3;
+ ITypeBinding[] parameterTypes;
+ if (binding instanceof IMethodBinding method) {
+ parameterTypes = method.getParameterTypes();
+ } else if (binding instanceof ITypeBinding type) {
+ parameterTypes = new ITypeBinding[] { type };
+ } else {
+ // unsupported binding...
+ return version;
+ }
+ for (ITypeBinding paramTypeBinding : parameterTypes) {
if (!paramTypeBinding.isAnnotation() || !visited.add(paramTypeBinding)) {
continue;
}
-
- for (IMethodBinding methodBinding : paramTypeBinding.getDeclaredMethods()) {
- if (!methodBinding.isAnnotationMember()) {
+ String prefix = getPrefix(paramTypeBinding);
+ if (prefix != null) {
+ version = DSAnnotationVersion.V1_4.max(version);
+ }
+ IMethodBinding[] declaredMethods = paramTypeBinding.getDeclaredMethods();
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ if (declaredMethods.length == 0) {
+ // a marker annotation! Actually the spec says it is not useful to have these on
+ // methods but the TCK do so...
+ // See https://github.com/osgi/osgi/issues/640
+ version = DSAnnotationVersion.V1_4.max(version);
+ IDSProperty property = factory.createProperty();
+ property.setPropertyName(NameGenerator.createClassPropertyName(paramTypeBinding.getName(), prefix));
+ property.setPropertyType(IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN);
+ property.setPropertyValue(String.valueOf(Boolean.TRUE));
+ properties.remove(property.getName()); // force re-insert (append)
+ properties.put(property.getName(), property);
continue;
}
-
- Object value = methodBinding.getDefaultValue();
- if (value == null) {
+ if (declaredMethods.length == 1 && "value".equals(declaredMethods[0].getName())) {
+ // a single member annotation
+ version = DSAnnotationVersion.V1_4.max(version);
+ IDSProperty property = createProperty(declaredMethods[0], prefix, factory);
+ property.setPropertyName(NameGenerator.createClassPropertyName(paramTypeBinding.getName(), prefix));
+ properties.remove(property.getName()); // force re-insert (append)
+ properties.put(property.getName(), property);
continue;
}
-
- ITypeBinding returnType = methodBinding.getReturnType();
- if (returnType.isArray() ? returnType.getElementType().isAnnotation() : returnType.isAnnotation()) {
- // TODO per spec we should report error, but we may have no annotation to report it on!
- continue;
- }
-
- IDSProperty property = factory.createProperty();
- property.setPropertyName(createPropertyName(methodBinding.getName()));
- property.setPropertyType(getPropertyType(returnType));
-
- if (returnType.isArray()) {
- StringBuilder body = new StringBuilder();
- for (Object item : ((Object[]) value)) {
- String itemValue = getPropertyValue(item);
- if (itemValue == null || (itemValue = itemValue.trim()).isEmpty()) {
- continue;
- }
-
- if (body.length() > 0) {
- body.append(TextUtil.getDefaultLineDelimiter());
- }
-
- body.append(itemValue);
- }
-
- removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
- property.setPropertyElemBody(body.toString());
- } else {
- property.setPropertyValue(getPropertyValue(value));
+ }
+ for (IMethodBinding methodBinding : declaredMethods) {
+ IDSProperty property = createProperty(methodBinding, prefix, factory);
+ if (property != null
+ && (property.getPropertyElemBody() != null || property.getPropertyValue() != null)) {
+ properties.remove(property.getName()); // force re-insert (append)
+ properties.put(property.getName(), property);
}
-
- properties.remove(property.getName()); // force re-insert (append)
- properties.put(property.getName(), property);
}
}
+ return version;
}
- private String createPropertyName(String name) {
- StringBuilder buf = new StringBuilder(name.length());
- char[] chars = name.toCharArray();
- for (int i = 0, n = chars.length; i < n; ++i) {
- if (chars[i] == '$') {
- if (i == n - 1 || chars[i + 1] != '$') {
- continue;
- }
+ private IDSProperty createProperty(IMethodBinding methodBinding, String prefix, IDSDocumentFactory factory) {
+ if (!methodBinding.isAnnotationMember()) {
+ return null;
+ }
+ ITypeBinding returnType = methodBinding.getReturnType();
+ if (returnType.isArray() ? returnType.getElementType().isAnnotation() : returnType.isAnnotation()) {
+ // TODO per spec we should report error, but we may have no annotation to report
+ // it on!
+ return null;
+ }
+ Object value = methodBinding.getDefaultValue();
+ String propertyName = NameGenerator.createPropertyName(methodBinding.getName(), prefix, specVersion);
+ String propertyType = getPropertyType(returnType);
+ IDSProperty property = factory.createProperty();
+ property.setPropertyName(propertyName);
+ property.setPropertyType(propertyType);
+ if (value == null) {
+ removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
+ } else {
+ if (returnType.isArray()) {
+ StringBuilder body = new StringBuilder();
+ for (Object item : ((Object[]) value)) {
+ String itemValue = getPropertyValue(item);
+ if (itemValue == null || (itemValue = itemValue.trim()).isEmpty()) {
+ continue;
+ }
- i++;
- } else if (chars[i] == '_') {
- if (i == n - 1 || chars[i + 1] != '_') {
- chars[i] = '.';
- } else {
- i++;
+ if (body.length() > 0) {
+ body.append(TextUtil.getDefaultLineDelimiter());
+ }
+
+ body.append(itemValue);
}
+ removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
+ property.setPropertyElemBody(body.toString());
+ } else {
+ property.setPropertyValue(getPropertyValue(value));
}
-
- buf.append(chars[i]);
}
-
- return buf.toString();
+ return property;
}
+
private String getPropertyType(ITypeBinding type) {
if (type.isArray()) {
return getPropertyType(type.getElementType());
@@ -1542,6 +1926,12 @@ private String getPropertyValue(Object value) {
if (value instanceof ITypeBinding) {
return ((ITypeBinding) value).getQualifiedName();
}
+ if (value instanceof Character character) {
+ // according to the spec a char must be encoded as its unicode point: For
+ // Character types, the conversion must be handled by Integer.valueOf method, a
+ // Character is always represented by its Unicode value.
+ return Integer.toString(character.charValue());
+ }
// everything else
return String.valueOf(value);
@@ -1601,7 +1991,7 @@ private void validateComponentProperty(Annotation annotation, String name, Strin
}
}
- private void validateComponentPropertyFiles(Annotation annotation, IProject project, String[] files) {
+ private void validateComponentPropertyFiles(String key, Annotation annotation, IProject project, String[] files) {
if (errorLevel.isIgnore()) {
return;
}
@@ -1610,7 +2000,8 @@ private void validateComponentPropertyFiles(Annotation annotation, IProject proj
String file = files[i];
IFile wsFile = PDEProject.getBundleRelativeFile(project, IPath.fromOSString(file));
if (!wsFile.exists()) {
- problemReporter.reportProblem(annotation, "properties", i, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyFile, file), file); //$NON-NLS-1$
+ problemReporter.reportProblem(annotation, key, i,
+ NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyFile, file, key), file); // $NON-NLS-1$
}
}
}
@@ -1645,6 +2036,10 @@ private void validateLifeCycleMethod(Annotation annotation, String methodName, M
return;
}
+ if (methodBinding.isConstructor()) {
+ problemReporter.reportProblem(annotation, methodName,
+ Messages.AnnotationProcessor_invalidLifecycleMethod_noMethod);
+ }
if (Modifier.isStatic(methodBinding.getModifiers())) {
problemReporter.reportProblem(annotation, methodName, Messages.AnnotationProcessor_invalidLifecycleMethod_static);
@@ -1674,11 +2069,11 @@ private void validateLifeCycleMethod(Annotation annotation, String methodName, M
String paramTypeName = paramTypeErasure.isMember() ? paramTypeErasure.getBinaryName() : paramTypeErasure.getQualifiedName();
boolean isDuplicate = false;
- if (paramTypeBinding.isAnnotation() && specVersion == DSAnnotationVersion.V1_3) {
+ if (paramTypeBinding.isAnnotation() && DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
if (!annotationParams.add(paramTypeBinding)) {
isDuplicate = true;
}
- } else if (Map.class.getName().equals(paramTypeName)) {
+ } else if (MAP_TYPE.equals(paramTypeName)) {
if (hasMap) {
isDuplicate = true;
} else {
@@ -1690,7 +2085,7 @@ private void validateLifeCycleMethod(Annotation annotation, String methodName, M
} else {
hasCompCtx = true;
}
- } else if (BundleContext.class.getName().equals(paramTypeName)) {
+ } else if (BUNDLE_CONTEXT.equals(paramTypeName)) {
if (hasBundleCtx) {
isDuplicate = true;
} else {
@@ -1728,7 +2123,8 @@ private IMethodBinding findLifeCycleMethod(ITypeBinding componentClass, String m
HashSet annotationParams = new HashSet<>(1);
for (ITypeBinding paramTypeBinding : paramTypeBindings) {
if (paramTypeBinding.isAnnotation()) {
- if (specVersion == DSAnnotationVersion.V1_3 && annotationParams.add(paramTypeBinding)) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)
+ && annotationParams.add(paramTypeBinding)) {
// component property type (multiple arguments allowed)
continue;
}
@@ -1740,7 +2136,7 @@ private IMethodBinding findLifeCycleMethod(ITypeBinding componentClass, String m
ITypeBinding paramTypeErasure = paramTypeBinding.getErasure();
String paramTypeName = paramTypeErasure.isMember() ? paramTypeErasure.getBinaryName() : paramTypeErasure.getQualifiedName();
- if (Map.class.getName().equals(paramTypeName)) {
+ if (MAP_TYPE.equals(paramTypeName)) {
if (hasMap) {
isInvalid = true;
} else {
@@ -1752,7 +2148,7 @@ private IMethodBinding findLifeCycleMethod(ITypeBinding componentClass, String m
} else {
hasCompCtx = true;
}
- } else if (BundleContext.class.getName().equals(paramTypeName)) {
+ } else if (BUNDLE_CONTEXT.equals(paramTypeName)) {
if (hasBundleCtx) {
isInvalid = true;
} else {
@@ -1782,4 +2178,91 @@ private IMethodBinding findLifeCycleMethod(ITypeBinding componentClass, String m
return null;
}
+
+ /**
+ * An injectable constructor is one annotated with @Activate
+ *
+ * @param type
+ * @param problemReporter2
+ * @return
+ */
+ private static boolean hasInjectableConstructor(TypeDeclaration type, ProblemReporter problemReporter) {
+ for (MethodDeclaration method : type.getMethods()) {
+ if (method.isConstructor()
+ && annotations(method.modifiers()).map(Annotation::resolveAnnotationBinding)
+ .anyMatch(AnnotationVisitor::isActivateAnnotation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasDefaultConstructor(TypeDeclaration type) {
+ boolean hasConstructor = false;
+ for (MethodDeclaration method : type.getMethods()) {
+ if (method.isConstructor()) {
+ hasConstructor = true;
+ if (Modifier.isPublic(method.getModifiers()) && method.parameters().isEmpty()) {
+ return true;
+ }
+ }
+ }
+
+ return !hasConstructor;
+ }
+
+ private static Stream annotations(List> modifiers) {
+ return modifiers.stream().filter(Annotation.class::isInstance).map(Annotation.class::cast);
+ }
+
+ private static boolean isActivateAnnotation(IAnnotationBinding binding) {
+ return binding != null && ACTIVATE_ANNOTATION.equals(binding.getAnnotationType().getQualifiedName());
+ }
+
+ private static boolean isReferenceAnnotation(IAnnotationBinding binding) {
+ return binding != null && REFERENCE_ANNOTATION.equals(binding.getAnnotationType().getQualifiedName());
+ }
+
+ private static boolean isComponentPropertyType(IAnnotationBinding binding) {
+ return binding != null
+ && COMPONENT_PROPERTY_TYPE_ANNOTATION.equals(binding.getAnnotationType().getQualifiedName());
+ }
+
+ /**
+ * Check if the given {@link ITypeBinding} is an Activation
+ * Object
+ *
+ * @param param the binding to check
+ * @return true
if this is an Activation Object, false
+ * otherwise
+ */
+ private static boolean isActivationObject(ITypeBinding param) {
+ String binaryName = param.getErasure().getBinaryName();
+ if (COMPONENT_CONTEXT.equals(binaryName) || BUNDLE_CONTEXT.equals(binaryName) || MAP_TYPE.equals(binaryName)) {
+ return true;
+ }
+ return param.isAnnotation();
+ }
+
+ /**
+ * Check if the given {@link ITypeBinding} is a Component
+ * Property Type
+ *
+ * @param param the binding to check
+ * @return true
if this is a Component Property Type
+ * false
otherwise
+ */
+ private static boolean isComponentPropertyType(ITypeBinding param) {
+ if (param != null) {
+ IAnnotationBinding[] annotations = param.getAnnotations();
+ for (IAnnotationBinding annotationAnnotation : annotations) {
+ if (isComponentPropertyType(annotationAnnotation)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
\ No newline at end of file
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ComponentActivationAnnotation.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ComponentActivationAnnotation.java
new file mode 100644
index 0000000000..2735e8f403
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ComponentActivationAnnotation.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.pde.ds.internal.annotations;
+
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+
+/**
+ * This capture the context of an @Activate
annotated method or
+ * field binding
+ */
+record ComponentActivationAnnotation(String activate, Annotation annotation, MethodDeclaration method,
+ IBinding binding) {
+
+ public boolean isMethod() {
+ if (binding instanceof IMethodBinding method) {
+ return !((IMethodBinding) binding).isConstructor();
+ }
+ return false;
+ }
+
+ public boolean isConstructor() {
+ if (binding instanceof IMethodBinding method) {
+ return ((IMethodBinding) binding).isConstructor();
+ }
+ return false;
+ }
+
+ public boolean isType() {
+ return binding instanceof ITypeBinding method;
+ }
+
+ public int parameterCount() {
+ if (binding instanceof IMethodBinding method) {
+ return method.getParameterNames().length;
+ }
+ return 0;
+ }
+
+}
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java
index 498e1b0e6e..71219a38b3 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java
@@ -15,6 +15,7 @@
package org.eclipse.pde.ds.internal.annotations;
import java.util.Arrays;
+import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
@@ -27,9 +28,16 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.dialogs.ControlEnableState;
import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.layout.LayoutConstants;
import org.eclipse.jface.preference.IPreferencePageContainer;
import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
@@ -74,7 +82,7 @@ public class DSAnnotationPropertyPage extends PropertyPage implements IWorkbench
private Text pathText;
- private Combo specVersionCombo;
+ private ComboViewer specVersionCombo;
private Combo errorLevelCombo;
@@ -232,12 +240,36 @@ public void widgetSelected(SelectionEvent e) {
specVersionLabel.setText(Messages.DSAnnotationPropertyPage_specVersionLabel_text);
specVersionLabel.setFont(JFaceResources.getDialogFont());
- specVersionCombo = new Combo(optionBlockControl, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.BORDER);
- specVersionCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
- specVersionCombo.setFont(JFaceResources.getDialogFont());
- specVersionCombo.add("1.3"); //$NON-NLS-1$
- specVersionCombo.add("1.2"); //$NON-NLS-1$
- specVersionCombo.select(0);
+ specVersionCombo = new ComboViewer(optionBlockControl, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.BORDER);
+ specVersionCombo.getControl().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+ specVersionCombo.getControl().setFont(JFaceResources.getDialogFont());
+ specVersionCombo.setContentProvider(ArrayContentProvider.getInstance());
+ specVersionCombo.setInput(List.of(DSAnnotationVersion.V1_2, DSAnnotationVersion.V1_3, DSAnnotationVersion.V1_4,
+ DSAnnotationVersion.V1_4));
+ specVersionCombo.setSelection(new StructuredSelection(DSAnnotationVersion.V1_5));
+ specVersionCombo.addSelectionChangedListener(new ISelectionChangedListener() {
+
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ DSAnnotationVersion version = (DSAnnotationVersion) event.getStructuredSelection().getFirstElement();
+ if (version == DSAnnotationVersion.V1_5) {
+ setMessage("Specification version " + version.getSpecificationVersion()
+ + " is currently not fully supported.", DialogPage.WARNING);
+ } else {
+ setMessage(null);
+ }
+
+ }
+ });
+ specVersionCombo.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof DSAnnotationVersion version) {
+ return version.getSpecificationVersion();
+ }
+ return "";
+ }
+ });
Label errorLevelLabel = new Label(optionBlockControl, SWT.LEFT);
errorLevelLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
@@ -279,7 +311,7 @@ private void refreshWidgets() {
boolean enableValue = prefs.getBoolean(Activator.PREF_ENABLED, false);
String pathValue = prefs.get(Activator.PREF_PATH, Activator.DEFAULT_PATH);
- String specVersion = prefs.get(Activator.PREF_SPEC_VERSION, DSAnnotationVersion.V1_3.name());
+ String specVersion = prefs.get(Activator.PREF_SPEC_VERSION, DSAnnotationVersion.V1_4.name());
String errorLevel = prefs.get(Activator.PREF_VALIDATION_ERROR_LEVEL, ValidationErrorLevel.error.name());
String missingUnbindMethodLevel = prefs.get(Activator.PREF_MISSING_UNBIND_METHOD_ERROR_LEVEL, errorLevel);
boolean generateBAPL = prefs.getBoolean(Activator.PREF_GENERATE_BAPL, true);
@@ -303,10 +335,10 @@ private void refreshWidgets() {
try {
specVersionEnum = DSAnnotationVersion.valueOf(specVersion);
} catch (IllegalArgumentException e) {
- specVersionEnum = DSAnnotationVersion.V1_3;
+ specVersionEnum = DSAnnotationVersion.V1_4;
}
- specVersionCombo.select(DSAnnotationVersion.V1_3.ordinal() - specVersionEnum.ordinal());
+ specVersionCombo.setSelection(new StructuredSelection(specVersionEnum));
errorLevelCombo.select(getEnumIndex(errorLevel, ValidationErrorLevel.values(), 0));
missingUnbindMethodCombo.select(getEnumIndex(missingUnbindMethodLevel, ValidationErrorLevel.values(), 0));
enableBAPLGeneration.setSelection(generateBAPL);
@@ -443,9 +475,9 @@ public boolean performOk() {
prefs.putBoolean(Activator.PREF_ENABLED, enableCheckbox.getSelection());
prefs.put(Activator.PREF_PATH, IPath.fromOSString(path).toString());
- DSAnnotationVersion[] versions = DSAnnotationVersion.values();
- int specVersionIndex = Math.max(Math.min(specVersionCombo.getSelectionIndex(), DSAnnotationVersion.V1_3.ordinal()), 0);
- prefs.put(Activator.PREF_SPEC_VERSION, versions[DSAnnotationVersion.V1_3.ordinal() - specVersionIndex].name());
+ DSAnnotationVersion version = (DSAnnotationVersion) specVersionCombo.getStructuredSelection()
+ .getFirstElement();
+ prefs.put(Activator.PREF_SPEC_VERSION, version.name());
ValidationErrorLevel[] levels = ValidationErrorLevel.values();
int errorLevelIndex = errorLevelCombo.getSelectionIndex();
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationVersion.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationVersion.java
index bcfe1c796b..622ae8a165 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationVersion.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationVersion.java
@@ -18,15 +18,23 @@
@SuppressWarnings("restriction")
public enum DSAnnotationVersion {
- V1_1(IDSConstants.NAMESPACE),
+ V1_0("1.0", "http://www.osgi.org/xmlns/scr/v1.0.0"),
- V1_2("http://www.osgi.org/xmlns/scr/v1.2.0"), //$NON-NLS-1$
+ V1_1("1.1", IDSConstants.NAMESPACE),
- V1_3("http://www.osgi.org/xmlns/scr/v1.3.0"); //$NON-NLS-1$
+ V1_2("1.2", "http://www.osgi.org/xmlns/scr/v1.2.0"), //$NON-NLS-1$
+
+ V1_3("1.3", "http://www.osgi.org/xmlns/scr/v1.3.0"), //$NON-NLS-1$
+
+ V1_4("1.4", "http://www.osgi.org/xmlns/scr/v1.4.0"), //$NON-NLS-1$
+
+ V1_5("1.5", "http://www.osgi.org/xmlns/scr/v1.5.0"); //$NON-NLS-1$
private final String namespace;
+ private String version;
- private DSAnnotationVersion(String namespace) {
+ private DSAnnotationVersion(String version, String namespace) {
+ this.version = version;
this.namespace = namespace;
}
@@ -42,6 +50,16 @@ public DSAnnotationVersion max(DSAnnotationVersion other) {
return this;
}
+ /**
+ * Compares this version with another one
+ *
+ * @param other
+ * @return true if this version is higher or equal to this version
+ */
+ public boolean isEqualOrHigherThan(DSAnnotationVersion other) {
+ return other.compareTo(this) >= 0;
+ }
+
public static DSAnnotationVersion fromNamespace(String namespace) {
for (DSAnnotationVersion value : values()) {
if (value.namespace.equals(namespace)) {
@@ -51,4 +69,13 @@ public static DSAnnotationVersion fromNamespace(String namespace) {
return null;
}
+
+ public String getSpecificationVersion() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ return version + " - " + namespace;
+ }
}
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSEnums.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSEnums.java
index abf0b4b292..f43a6c9b3d 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSEnums.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSEnums.java
@@ -51,6 +51,13 @@ public class DSEnums {
"UPDATE", "update", //
"REPLACE", "replace");
+ public static final Map COLLECTION_TYPE_OPTION = Map.of( //
+ "SERVICE", "service", //
+ "REFERENCE", "reference", //
+ "SERVICEOBJECTS", "serviceobjects", //
+ "PROPERTIES", "properties", //
+ "TUPLE", "tuple");
+
private DSEnums() {
}
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Messages.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Messages.java
index 8e0d9c94c4..306a5ce9ce 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Messages.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Messages.java
@@ -59,8 +59,16 @@ public class Messages extends NLS {
public static String AnnotationProcessor_invalidComponentImplementationClass;
+ public static String AnnotationProcessor_invalidCompImplClass_compatibleConstructor;
+
public static String AnnotationProcessor_invalidComponentName;
+ public static String AnnotationProcessor_invalidActivate;
+
+ public static String AnnotationProcessor_invalidActivateField;
+
+ public static String AnnotationProcessor_invalidConstructorArgument;
+
public static String AnnotationProcessor_invalidComponentProperty_nameRequired;
public static String AnnotationProcessor_invalidComponentProperty_valueRequired;
@@ -73,6 +81,8 @@ public class Messages extends NLS {
public static String AnnotationProcessor_invalidComponentService;
+ public static String AnnotationProcessor_invalidLifecycleMethod_noMethod;
+
public static String AnnotationProcessor_invalidLifecycleMethod_static;
public static String AnnotationProcessor_invalidLifeCycleMethodParameterType;
@@ -115,6 +125,8 @@ public class Messages extends NLS {
public static String AnnotationProcessor_invalidReference_staticField;
+ public static String AnnotationProcessor_invalidActivate_staticField;
+
public static String AnnotationProcessor_invalidReference_serviceType;
public static String AnnotationProcessor_invalidReference_serviceUnknown;
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/NameGenerator.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/NameGenerator.java
new file mode 100644
index 0000000000..ece613610c
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/NameGenerator.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.pde.ds.internal.annotations;
+
+public class NameGenerator {
+
+ public static String createClassPropertyName(String name, String prefix) {
+ // see
+ // https://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#service.component-component.property.mapping
+ // > single-element annotation
+ StringBuilder buf = new StringBuilder(name.length() * 2);
+ if (prefix != null) {
+ // rule 4: If the component property type declares a PREFIX_ field whose value
+ // is a compile-time constant String, then the property name is prefixed with
+ // the value of the PREFIX_ field.
+ buf.append(prefix);
+ }
+ char[] chars = name.toCharArray();
+ char last = 'X';
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (Character.isLowerCase(last) && Character.isUpperCase(c)) {
+ // Rule 1: When a lower case character is followed by an upper case character, a
+ // full stop ('.' \u002E) is inserted between them.
+ buf.append('.');
+ }
+ // Rule 2: Each upper case character is converted to lower case.
+ buf.append(Character.toLowerCase(c));
+ // Rule 3: All other characters are unchanged.
+ // --> nothing to do
+ last = c;
+ }
+ return buf.toString();
+ }
+
+ public static String createPropertyName(String name, String prefix, DSAnnotationVersion specVersion) {
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ // See
+ // https://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#service.component-component.property.mapping
+ StringBuilder sb;
+ if (prefix == null) {
+ sb = new StringBuilder(name.length());
+ } else {
+ // Rule 4: If the component property type declares a PREFIX_ field whose value
+ // is a compile-time constant String, then the property name is prefixed with
+ // the value of the PREFIX_ field.
+ sb = new StringBuilder(name.length() + prefix.length());
+ sb.append(prefix);
+ }
+ char[] chars = name.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (c == '$') {
+ // Rule 1: A single dollar sign ('$' \u0024) is removed unless it is followed by
+ if (i < chars.length - 1) {
+ char n = chars[i + 1];
+ if (n == '_') {
+ // Rule 1.1: A low line ('_' \u005F) and a dollar sign in which case the three
+ // consecutive characters ("$_$") are converted to a single hyphen-minus ('-'
+ // \u002D).
+ if (i < chars.length - 2 && chars[i + 2] == '$') {
+ i = i + 2;
+ sb.append('-');
+ }
+ } else if (n == '$') {
+ // Rule 1.2 : another dollar sign in which case the two consecutive dollar signs
+ // ("$$") are converted to a single dollar sign.
+ i = i + 1;
+ sb.append('$');
+ }
+ }
+ continue;
+ }
+ if (c == '_') {
+ // Rule 2: A single low line ('_' \u005F) is converted into a full stop ('.'
+ // \u002E) unless is it followed by an-
+ // other low line in which case the two consecutive low lines ("__") are
+ // converted to a single low
+ // line.
+ char n = chars[i + 1];
+ if (i < chars.length - 1 && n == '_') {
+ i = i + 1;
+ sb.append('_');
+ } else {
+ sb.append('.');
+ }
+ continue;
+ }
+ // Rule 3: All other characters are unchanged.
+ sb.append(chars[i]);
+ }
+ return sb.toString();
+ } else {
+ StringBuilder sb = new StringBuilder(name.length());
+ char[] chars = name.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (c == '$') {
+ // Rule 1: A single dollar sign ('$' \u0024) is removed unless it is followed by
+ // another dollar sign in which
+ // case the two consecutive dollar signs ("$$") are converted to a single dollar
+ // sign.
+ if (i < chars.length - 1 && chars[i + 1] == '$') {
+ i = i + 1;
+ sb.append('$');
+ }
+ continue;
+ }
+ if (c == '_') {
+ // Rule 2: A single low line ('_' \u005F) is converted into a full stop ('.'
+ // \u002E) unless is it followed by an-
+ // other low line in which case the two consecutive low lines ("__") are
+ // converted to a single low
+ // line.
+ if (i < chars.length - 1 && chars[i + 1] == '_') {
+ i = i + 1;
+ sb.append('_');
+ } else {
+ sb.append('.');
+ }
+ continue;
+ }
+ // Rule 3: All other characters are unchanged.
+ sb.append(chars[i]);
+ }
+ return sb.toString();
+ }
+ }
+
+}
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ReferenceProcessor.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ReferenceProcessor.java
index 58fc1706b8..ec8a78f464 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ReferenceProcessor.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/ReferenceProcessor.java
@@ -21,8 +21,8 @@
import java.util.Map;
import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
-import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
@@ -39,6 +39,8 @@
@SuppressWarnings("restriction")
public class ReferenceProcessor {
+ private static final String ATTRIBUTE_COLLECTION_TYPE = "collectionType";
+
private static final String COMPONENT_SERVICE_OBJECTS = "org.osgi.service.component.ComponentServiceObjects"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_POLICY_OPTION = "policy-option"; //$NON-NLS-1$
@@ -53,6 +55,8 @@ public class ReferenceProcessor {
private static final String ATTRIBUTE_REFERENCE_FIELD_COLLECTION_TYPE = "field-collection-type"; //$NON-NLS-1$
+ private static final String VALUE_REFERENCE_FIELD_COLLECTION_TYPE_SERVICE = "service"; //$NON-NLS-1$
+
private static final String VALUE_REFERENCE_FIELD_OPTION_REPLACE = DSEnums.getFieldOption("REPLACE"); //$NON-NLS-1$
private static final String VALUE_REFERENCE_FIELD_OPTION_UPDATE = DSEnums.getFieldOption("UPDATE"); //$NON-NLS-1$
@@ -86,7 +90,7 @@ public DSAnnotationVersion processReference(IDSReference reference, MethodDeclar
if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$
serviceType = (ITypeBinding) value;
if (!errorLevel.isIgnore() && argTypes.length > 0) {
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
for (ITypeBinding argType : argTypes) {
if (!isValidArgumentForService(argType, serviceType)) {
problemReporter.reportProblem(annotation, "service", NLS.bind(Messages.AnnotationProcessor_invalidReference_serviceType, argType.getName(), serviceType.getName()), argType.getName(), serviceType.getName()); //$NON-NLS-1$
@@ -104,7 +108,7 @@ public DSAnnotationVersion processReference(IDSReference reference, MethodDeclar
}
}
} else if (argTypes.length > 0) {
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
serviceType = null;
for (ITypeBinding argType : argTypes) {
String erasure = argType.getErasure().getBinaryName();
@@ -241,7 +245,7 @@ public DSAnnotationVersion processReference(IDSReference reference, MethodDeclar
}
String referenceScope = null;
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding referenceScopeBinding = (IVariableBinding) value;
referenceScope = DSEnums.getReferenceScope(referenceScopeBinding.getName());
@@ -273,7 +277,7 @@ private boolean isValidArgumentForService(ITypeBinding argType, ITypeBinding ser
ITypeBinding[] typeArgs;
return ((ServiceReference.class.getName().equals(erasure) || COMPONENT_SERVICE_OBJECTS.equals(erasure))
&& ((typeArgs = argType.getTypeArguments()).length == 0 || serviceType.isAssignmentCompatible(typeArgs[0])))
- || serviceType.isAssignmentCompatible(argType)
+ || isValidServiceAssignment(argType, serviceType, specVersion)
|| Map.class.getName().equals(erasure);
}
@@ -492,7 +496,11 @@ private void updateFieldAttributes(
}
}
- public void processReference(IDSReference reference, FieldDeclaration field, IVariableBinding fieldBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map params, Map names) {
+ public DSAnnotationVersion processReference(IDSReference reference, ASTNode field, int modifiers,
+ IVariableBinding fieldBinding,
+ Annotation annotation, IAnnotationBinding annotationBinding, Map params,
+ Map names) {
+ DSAnnotationVersion requiredVersion = DSAnnotationVersion.V1_3;
String cardinality = null;
Object value;
if ((value = params.get("cardinality")) instanceof IVariableBinding) { //$NON-NLS-1$
@@ -522,7 +530,8 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
}
ITypeBinding serviceType;
- if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$
+ Object serviceProperty = params.get("service");
+ if ((value = serviceProperty) instanceof ITypeBinding) { // $NON-NLS-1$
serviceType = (ITypeBinding) value;
if (!errorLevel.isIgnore()) {
ITypeBinding targetType = fieldType;
@@ -565,8 +574,12 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
String service = serviceType == null ? null : serviceType.getBinaryName();
- String fieldCollectionType = null;
- if (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
+ String fieldCollectionType = DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)
+ ? getCollectionType(params)
+ : null;
+ if (fieldCollectionType != null) {
+ requiredVersion = requiredVersion.max(DSAnnotationVersion.V1_4);
+ } else if (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality)) {
if (collectionType == null) {
collectionType = determineCollectionType(field.getAST(), fieldType);
@@ -574,6 +587,11 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
if (collectionType.getElementType() != null) {
fieldCollectionType = getFieldCollectionType(collectionType);
+
+ }
+ if (fieldCollectionType == null && DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ // TODO workaround for https://github.com/osgi/osgi/issues/641
+ fieldCollectionType = "service";
}
}
@@ -601,7 +619,7 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyBinding = (IVariableBinding) value;
policy = DSEnums.getReferencePolicy(policyBinding.getName());
- } else if (Modifier.isVolatile(field.getModifiers())) {
+ } else if (Modifier.isVolatile(modifiers)) {
policy = IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC;
}
@@ -629,7 +647,7 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
fieldOption = DSEnums.getFieldOption(fieldOptionBinding.getName());
if (!errorLevel.isIgnore()) {
if (VALUE_REFERENCE_FIELD_OPTION_REPLACE.equals(fieldOption)) {
- if (Modifier.isFinal(field.getModifiers())) {
+ if (Modifier.isFinal(modifiers)) {
problemReporter.reportProblem(annotation, "fieldOption", Messages.AnnotationProcessor_invalidReference_fieldFinal_fieldOption, fieldOption); //$NON-NLS-1$
}
} else if (VALUE_REFERENCE_FIELD_OPTION_UPDATE.equals(fieldOption)) {
@@ -644,7 +662,7 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
if (IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC.equals(policy)
&& (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality))
- && Modifier.isFinal(field.getModifiers())) {
+ && Modifier.isFinal(modifiers)) {
fieldOption = VALUE_REFERENCE_FIELD_OPTION_UPDATE;
}
}
@@ -667,6 +685,16 @@ public void processReference(IDSReference reference, FieldDeclaration field, IVa
updateAttributes(reference, name, service, cardinality, policy, target, policyOption, referenceScope);
updateFieldAttributes(reference, fieldName, fieldOption, fieldCollectionType);
+
+ return requiredVersion;
+ }
+
+ private String getCollectionType(Map params) {
+ Object object = params.get(ATTRIBUTE_COLLECTION_TYPE);
+ if (object instanceof IVariableBinding variable) {
+ return DSEnums.COLLECTION_TYPE_OPTION.get(variable.getName());
+ }
+ return null;
}
private FieldCollectionTypeDescriptor determineCollectionType(AST ast, ITypeBinding type) {
@@ -709,7 +737,7 @@ private boolean isValidFieldForService(ITypeBinding fieldType, ITypeBinding serv
|| Map.class.getName().equals(erasure)
|| (Map.Entry.class.getName().equals(erasure)
&& ((typeArgs = fieldType.getTypeArguments()).length < 2 || (Map.class.getName().equals(typeArgs[0].getErasure().getBinaryName()) && serviceType.isAssignmentCompatible(typeArgs[1]))))
- || serviceType.isAssignmentCompatible(fieldType);
+ || isValidServiceAssignment(fieldType, serviceType, specVersion);
}
private ITypeBinding getFieldServiceType(AST ast, ITypeBinding type) {
@@ -787,6 +815,7 @@ private String getFieldCollectionType(FieldCollectionTypeDescriptor collectionTy
} else if (Map.Entry.class.getName().equals(erasure)) {
fieldCollectionType = "tuple"; //$NON-NLS-1$
}
+ // TODO "service"
return fieldCollectionType;
}
@@ -923,7 +952,7 @@ private void validateReferenceBindMethod(Annotation annotation, ITypeBinding ser
}
ITypeBinding[] argTypes = methodBinding.getParameterTypes();
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
if (argTypes.length == 0) {
problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidReference_bindMethodNoArgs, serviceType == null ? Messages.AnnotationProcessor_unknownServiceTypeLabel : serviceType.getName()));
} else if (serviceType != null) {
@@ -931,7 +960,7 @@ private void validateReferenceBindMethod(Annotation annotation, ITypeBinding ser
String erasure = argType.getErasure().getBinaryName();
if (!ServiceReference.class.getName().equals(erasure)
&& !COMPONENT_SERVICE_OBJECTS.equals(erasure)
- && !(serviceType == null || serviceType.isAssignmentCompatible(argType))
+ && !(serviceType == null || isValidServiceAssignment(argType, serviceType, specVersion))
&& !Map.class.getName().equals(erasure)) {
problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidReference_invalidBindMethodArg, argType.getName(), serviceType == null ? Messages.AnnotationProcessor_unknownServiceTypeLabel : serviceType.getName()), argType.getName());
}
@@ -992,7 +1021,7 @@ private IMethodBinding findReferenceMethod(ITypeBinding componentClass, ITypeBin
&& testedClass.getPackage().isEqualTo(componentClass.getPackage())))) {
ITypeBinding[] paramTypes = declaredMethod.getParameterTypes();
- if (specVersion == DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion)) {
for (int i = 0; i < paramTypes.length; ++i) {
ITypeBinding paramType = paramTypes[i];
int priorityOffset = i == 0 ? 10 : 0;
@@ -1090,7 +1119,7 @@ private DSAnnotationVersion determineRequiredVersion(
}
DSAnnotationVersion requiredVersion;
- if (specVersion == DSAnnotationVersion.V1_3 &&
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion) &&
(reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_SCOPE) != null
|| reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_FIELD) != null
|| reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_FIELD_OPTION) != null
@@ -1103,7 +1132,7 @@ private DSAnnotationVersion determineRequiredVersion(
requiredVersion = DSAnnotationVersion.V1_1;
}
- if (specVersion == DSAnnotationVersion.V1_3 && requiredVersion != DSAnnotationVersion.V1_3) {
+ if (DSAnnotationVersion.V1_3.isEqualOrHigherThan(specVersion) && requiredVersion != DSAnnotationVersion.V1_3) {
// check if any one of the event methods *don't* have legacy-compatible signature
String bind = methodParams.getBind();
IMethodBinding bindMethod = methodParams.getBindMethod();
@@ -1207,4 +1236,24 @@ public IMethodBinding getUnbindMethod() {
}
}
+
+ private static boolean isValidServiceAssignment(ITypeBinding targetType, ITypeBinding serviceType,
+ DSAnnotationVersion specVersion) {
+ if (serviceType.isAssignmentCompatible(targetType)) {
+ // thats easy...
+ return true;
+ }
+ if (DSAnnotationVersion.V1_4.isEqualOrHigherThan(specVersion)) {
+ String serviceErasuer = serviceType.getErasure().getBinaryName();
+ if ("org.osgi.service.log.LoggerFactory".equals(serviceErasuer)) {
+ String fieldErasure = targetType.getErasure().getBinaryName();
+ if ("org.osgi.service.log.Logger".equals(fieldErasure)
+ || "org.osgi.service.log.FormatterLogger".equals(fieldErasure)) {
+ // valid per specification...
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/messages.properties b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/messages.properties
index 4297feef2a..131243e569 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/messages.properties
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/messages.properties
@@ -25,6 +25,7 @@ AnnotationProcessor_invalidCompImplClass_annotation=Invalid component implementa
AnnotationProcessor_invalidCompImplClass_enumeration=Invalid component implementation class ''{0}'': enumeration type.
AnnotationProcessor_invalidCompImplClass_interface=Invalid component implementation class ''{0}'': interface type.
AnnotationProcessor_invalidCompImplClass_noDefaultConstructor=Invalid component implementation class ''{0}'': no default constructor.
+AnnotationProcessor_invalidCompImplClass_compatibleConstructor=Invalid component implementation class ''{0}'': no default or @Activate annotated constructor.
AnnotationProcessor_invalidCompImplClass_notPublic=Invalid component implementation class ''{0}'': not a public class.
AnnotationProcessor_invalidCompImplClass_notTopLevel=Invalid component implementation class ''{0}'': not a primary class or static class nested in primary type.
AnnotationProcessor_invalidComponentConfigurationPid=Invalid component configuration PID: {0}
@@ -32,12 +33,16 @@ AnnotationProcessor_invalidComponentDescriptorNamespace=Invalid component descri
AnnotationProcessor_invalidComponentFactoryName=Invalid component factory name: {0}
AnnotationProcessor_invalidComponentImplementationClass=Invalid component implementation class: {0}
AnnotationProcessor_invalidComponentName=Invalid component name: {0}
+AnnotationProcessor_invalidActivate=Invalid usage of @Activate
+AnnotationProcessor_invalidActivateField=Field {0} is not an Activation Object, must be either org.osgi.service.component.ComponentContext, org.osgi.framework.BundleContext, java.util.Map or a component property type
+AnnotationProcessor_invalidConstructorArgument=Invalid constructor parameter '{0}' at index {1}, must be either @Reference annotated, org.osgi.service.component.ComponentContext, org.osgi.framework.BundleContext, or java.util.Map
AnnotationProcessor_invalidComponentProperty_nameRequired=Invalid component property: name is required.
AnnotationProcessor_invalidComponentProperty_valueRequired=Invalid component property: value is required.
-AnnotationProcessor_invalidComponentPropertyFile=Component property file ''{0}'' does not exist.
+AnnotationProcessor_invalidComponentPropertyFile=Component {1} file ''{0}'' does not exist.
AnnotationProcessor_invalidComponentPropertyType=Invalid component property type: {0}. Supported types are String (or empty), Long, Double, Float, Integer, Byte, Character, Boolean and Short.
AnnotationProcessor_invalidComponentPropertyValue=Invalid {0} value: {1}
AnnotationProcessor_invalidComponentService=Component does not extend or implement type: {0}
+AnnotationProcessor_invalidLifecycleMethod_noMethod=Only allowed for methods.
AnnotationProcessor_invalidLifecycleMethod_static=Lifecycle method cannot be static.
AnnotationProcessor_invalidLifeCycleMethodParameterType=Invalid {0} method argument type: {1}; must be org.osgi.service.component.ComponentContext, org.osgi.framework.BundleContext, or java.util.Map.
AnnotationProcessor_invalidLifeCycleMethodReturnType=Invalid {0} method return type: {1}; must be void.
@@ -59,6 +64,7 @@ AnnotationProcessor_invalidReference_invalidBindMethodArg=Invalid bind method ar
AnnotationProcessor_invalidReference_missingRequiredParam=Missing required parameter: {0}
AnnotationProcessor_invalidReference_staticBindMethod=Reference bind method cannot be static.
AnnotationProcessor_invalidReference_staticField=Reference field cannot be static.
+AnnotationProcessor_invalidActivate_staticField=Activate field cannot be static.
AnnotationProcessor_invalidReference_serviceType=Service type {1} incompatible with argument type {0}.
AnnotationProcessor_invalidReference_serviceUnknown=Unable to determine service type from method signature. Please specify service explicitly.
AnnotationProcessor_invalidReference_unbindMethod=No suitable unbind method named ''{0}'' found in implementation class hierarchy.
diff --git a/ds/org.eclipse.pde.ds.annotations/src_test/org/eclipse/pde/ds/annotations/NameGeneratorTest.java b/ds/org.eclipse.pde.ds.annotations/src_test/org/eclipse/pde/ds/annotations/NameGeneratorTest.java
new file mode 100644
index 0000000000..892b73d766
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.annotations/src_test/org/eclipse/pde/ds/annotations/NameGeneratorTest.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.pde.ds.annotations;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.pde.ds.internal.annotations.DSAnnotationVersion;
+import org.eclipse.pde.ds.internal.annotations.NameGenerator;
+import org.junit.jupiter.api.Test;
+
+class NameGeneratorTest {
+
+ @Test
+ void test13() {
+ assertEquals("myProperty143", NameGenerator.createPropertyName("myProperty143", null, DSAnnotationVersion.V1_3));
+ assertEquals("new", NameGenerator.createPropertyName("$new", null, DSAnnotationVersion.V1_3));
+ assertEquals("my$prop", NameGenerator.createPropertyName("my$$prop", null, DSAnnotationVersion.V1_3));
+ assertEquals("dot.prop", NameGenerator.createPropertyName("dot_prop", null, DSAnnotationVersion.V1_3));
+ assertEquals(".secret", NameGenerator.createPropertyName("_secret", null, DSAnnotationVersion.V1_3));
+ assertEquals("another_prop", NameGenerator.createPropertyName("another__prop", null, DSAnnotationVersion.V1_3));
+ assertEquals("three_.prop", NameGenerator.createPropertyName("three___prop", null, DSAnnotationVersion.V1_3));
+ assertEquals("four._prop", NameGenerator.createPropertyName("four_$__prop", null, DSAnnotationVersion.V1_3));
+ assertEquals("five..prop", NameGenerator.createPropertyName("five_$_prop", null, DSAnnotationVersion.V1_3));
+ }
+
+ @Test
+ void test14() {
+ assertEquals("myProperty143", NameGenerator.createPropertyName("myProperty143", null, DSAnnotationVersion.V1_4));
+ assertEquals("new", NameGenerator.createPropertyName("$new", null, DSAnnotationVersion.V1_4));
+ assertEquals("my$prop", NameGenerator.createPropertyName("my$$prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("dot.prop", NameGenerator.createPropertyName("dot_prop", null, DSAnnotationVersion.V1_4));
+ assertEquals(".secret", NameGenerator.createPropertyName("_secret", null, DSAnnotationVersion.V1_4));
+ assertEquals("another_prop", NameGenerator.createPropertyName("another__prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("three_.prop", NameGenerator.createPropertyName("three___prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("four._prop", NameGenerator.createPropertyName("four_$__prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("five..prop", NameGenerator.createPropertyName("five_$_prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("six-prop", NameGenerator.createPropertyName("six$_$prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("seven$.prop", NameGenerator.createPropertyName("seven$$_$prop", null, DSAnnotationVersion.V1_4));
+ assertEquals("pre.myProperty143",
+ NameGenerator.createPropertyName("myProperty143", "pre.", DSAnnotationVersion.V1_4));
+ assertEquals("service.ranking", NameGenerator.createClassPropertyName("ServiceRanking", null));
+ assertEquals("some_name", NameGenerator.createClassPropertyName("Some_Name", null));
+ assertEquals("osgi.property", NameGenerator.createClassPropertyName("OSGiProperty", null));
+ }
+
+}
diff --git a/ds/org.eclipse.pde.ds.core/plugin.xml b/ds/org.eclipse.pde.ds.core/plugin.xml
index 7ac5ea9c84..e25229e127 100644
--- a/ds/org.eclipse.pde.ds.core/plugin.xml
+++ b/ds/org.eclipse.pde.ds.core/plugin.xml
@@ -25,10 +25,30 @@
priority="high">
+
+
+
+
+
+
+
+
+
+
diff --git a/ds/org.eclipse.pde.ds.core/src/org/eclipse/pde/internal/ds/core/builders/DSErrorReporter.java b/ds/org.eclipse.pde.ds.core/src/org/eclipse/pde/internal/ds/core/builders/DSErrorReporter.java
index cb60d1d106..01f136d7e3 100644
--- a/ds/org.eclipse.pde.ds.core/src/org/eclipse/pde/internal/ds/core/builders/DSErrorReporter.java
+++ b/ds/org.eclipse.pde.ds.core/src/org/eclipse/pde/internal/ds/core/builders/DSErrorReporter.java
@@ -358,7 +358,12 @@ private void validatePropertySpecificTypeValue(String type, String value, Elemen
} else {
// Validate Chars
if (type.equals(IDSConstants.VALUE_PROPERTY_TYPE_CHAR)) {
- if (value.length() > 1) {
+ // The spec says: For Character types, the conversion must
+ // be handled by Integer.valueOf method, a Character is
+ // always represented by its Unicode value.
+ try {
+ Integer.valueOf(value);
+ } catch (NumberFormatException e) {
reportPropertyTypeCastException(element, value, type);
}
}
diff --git a/ds/org.eclipse.pde.ds.tck/.classpath b/ds/org.eclipse.pde.ds.tck/.classpath
new file mode 100644
index 0000000000..66d7021dd1
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/ds/org.eclipse.pde.ds.tck/.project b/ds/org.eclipse.pde.ds.tck/.project
new file mode 100644
index 0000000000..3fc41e1669
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/.project
@@ -0,0 +1,33 @@
+
+
+ org.eclipse.pde.ds.tck
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.pde.ds.core.builder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.core.resources.prefs b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..99f26c0203
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.jdt.core.prefs b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..62ef3488cc
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.m2e.core.prefs b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000000..f897a7f1cb
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.pde.ds.annotations.prefs b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.pde.ds.annotations.prefs
new file mode 100644
index 0000000000..c829ba88a6
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/.settings/org.eclipse.pde.ds.annotations.prefs
@@ -0,0 +1,7 @@
+dsVersion=V1_5
+eclipse.preferences.version=1
+enabled=true
+generateBundleActivationPolicyLazy=true
+path=OSGI-INF
+validationErrorLevel=error
+validationErrorLevel.missingImplicitUnbindMethod=error
diff --git a/ds/org.eclipse.pde.ds.tck/META-INF/MANIFEST.MF b/ds/org.eclipse.pde.ds.tck/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..f27a3cf57c
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/META-INF/MANIFEST.MF
@@ -0,0 +1,51 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Component XML TCK
+Bundle-SymbolicName: org.eclipse.pde.ds.tck
+Bundle-Version: 1.0.0.qualifier
+Import-Package: org.osgi.framework,
+ org.osgi.service.component,
+ org.osgi.service.component.propertytypes,
+ org.osgi.service.log
+Service-Component: OSGI-INF/org.osgi.impl.bundle.component.annotations.HelloWorld10.xml,
+ OSGI-INF/testActivationFields.xml,
+ OSGI-INF/testComponentPropertyTypes.xml,
+ OSGI-INF/testComponentReferences.xml,
+ OSGI-INF/testConfigPid.xml,
+ OSGI-INF/testConfigPidMultiple.xml,
+ OSGI-INF/testConfigPolicyIgnore.xml,
+ OSGI-INF/testConfigPolicyOptional.xml,
+ OSGI-INF/testConfigPolicyRequire.xml,
+ OSGI-INF/testConstructorInjection.xml,
+ OSGI-INF/testDelayed.xml,
+ OSGI-INF/testDisabled.xml,
+ OSGI-INF/testEnabled.xml,
+ OSGI-INF/testFactory.xml,
+ OSGI-INF/testFactoryProperties.xml,
+ OSGI-INF/testFieldReferences.xml,
+ OSGI-INF/testHelloWorld11.xml,
+ OSGI-INF/testHelloWorld12.xml,
+ OSGI-INF/testHelloWorld13.xml,
+ OSGI-INF/testHelloWorld14.xml,
+ OSGI-INF/testImmediate.xml,
+ OSGI-INF/testLoggerComponent.xml,
+ OSGI-INF/testNameMapping.xml,
+ OSGI-INF/testNoInheritService.xml,
+ OSGI-INF/testNoService.xml,
+ OSGI-INF/testNoServiceFactory.xml,
+ OSGI-INF/testProperties.xml,
+ OSGI-INF/testPropertyOrdering.xml,
+ OSGI-INF/testPropertyOrdering14.xml,
+ OSGI-INF/testReferenceNames.xml,
+ OSGI-INF/testReferenceScopes.xml,
+ OSGI-INF/testReferenceService.xml,
+ OSGI-INF/testReferences.xml,
+ OSGI-INF/testService.xml,
+ OSGI-INF/testServiceBundle.xml,
+ OSGI-INF/testServiceFactory.xml,
+ OSGI-INF/testServicePrototype.xml,
+ OSGI-INF/testServiceSingleton.xml
+Bundle-Vendor: Eclipse.org
+Automatic-Module-Name: org.eclipse.pde.ds.tck
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-17
diff --git a/ds/org.eclipse.pde.ds.tck/OSGI-INF/.gitignore b/ds/org.eclipse.pde.ds.tck/OSGI-INF/.gitignore
new file mode 100644
index 0000000000..b878e882ac
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/OSGI-INF/.gitignore
@@ -0,0 +1 @@
+/*.xml
diff --git a/ds/org.eclipse.pde.ds.tck/OSGI-INF/vendor.properties b/ds/org.eclipse.pde.ds.tck/OSGI-INF/vendor.properties
new file mode 100644
index 0000000000..e4c57dd8f5
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/OSGI-INF/vendor.properties
@@ -0,0 +1 @@
+## FIXME this is just a dummy file to account for https://github.com/osgi/osgi/issues/635
\ No newline at end of file
diff --git a/ds/org.eclipse.pde.ds.tck/README.MD b/ds/org.eclipse.pde.ds.tck/README.MD
new file mode 100644
index 0000000000..0550ce0e41
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/README.MD
@@ -0,0 +1,5 @@
+# OSGi Technical Compatibility Kit for DS Annotations
+
+This project executes the official OSGi TCK for DS annotations, it can be run with thefollwoing command from the root of the project:
+
+`mvn clean verify -pl :org.eclipse.pde.ds.tck,:org.eclipse.pde.ds.annotations -Ptck -am -Dtycho.localArtifacts=ignore`
diff --git a/ds/org.eclipse.pde.ds.tck/build.properties b/ds/org.eclipse.pde.ds.tck/build.properties
new file mode 100644
index 0000000000..c6035d8a0e
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/build.properties
@@ -0,0 +1,5 @@
+source.. = tck/OSGI-INF/impl-src
+output.. = target/classes
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/
diff --git a/ds/org.eclipse.pde.ds.tck/pom.xml b/ds/org.eclipse.pde.ds.tck/pom.xml
new file mode 100644
index 0000000000..61311a0d59
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/pom.xml
@@ -0,0 +1,146 @@
+
+
+ 4.0.0
+
+ org.eclipse.pde
+ eclipse.pde
+ 4.31.0-SNAPSHOT
+ ../../
+
+ org.eclipse.pde.ds.tck
+ 1.0.0-SNAPSHOT
+ eclipse-plugin
+
+
+
+
+ org.eclipse.tycho
+ target-platform-configuration
+
+
+
+
+ eclipse-plugin
+ org.osgi.service.component.annotations
+ [1.5, 1.6)
+
+
+ eclipse-plugin
+ org.eclipse.pde.ds.annotations
+ 0.0.0
+
+
+
+
+
+
+ p2-installable-unit
+ org.eclipse.osgi.services
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ unpack-tck-sourcec
+ process-resources
+
+ unpack-dependencies
+
+
+ org.osgi.test.cases.component.annotations
+ ${basedir}/tck
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-eclipse-plugin
+ ${tycho.version}
+
+
+ generate-xmls
+
+ eclipse-build
+
+ compile
+
+ true
+
+ false
+
+ org.eclipse.pde.core
+ org.eclipse.pde.ds.annotations
+
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-plugin
+
+
+
+ default-compile
+ none
+
+
+
+
+ org.eclipse.tycho
+ tycho-ds-plugin
+ ${tycho.version}
+
+
+
+ default-declarative-services
+ none
+
+
+
+
+ org.eclipse.tycho
+ tycho-surefire-plugin
+
+
+ execute-tck
+
+ bnd-test
+ verify
+
+
+
+ org.osgi.test.cases.component.annotations
+
+ false
+ false
+ true
+ true
+ ${project.build.directory}/tck-results
+
+ org.eclipse.pde.ds.tck
+
+
+
+
+
+
+
+
+
+
+ org.osgi
+ org.osgi.test.cases.component.annotations
+ 8.1.0
+
+
+
+
\ No newline at end of file
diff --git a/ds/org.eclipse.pde.ds.tck/tck/.gitignore b/ds/org.eclipse.pde.ds.tck/tck/.gitignore
new file mode 100644
index 0000000000..f59ec20aab
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.tck/tck/.gitignore
@@ -0,0 +1 @@
+*
\ No newline at end of file
diff --git a/ds/pom.xml b/ds/pom.xml
index 9552a63499..8971bd6552 100644
--- a/ds/pom.xml
+++ b/ds/pom.xml
@@ -25,4 +25,13 @@
org.eclipse.pde.ds.ui
org.eclipse.pde.ds.annotations
+
+
+
+ tck
+
+ org.eclipse.pde.ds.tck
+
+
+