This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch bugfix/remove-empty-properties-annotation in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit 062fab19fead819b7b7d764fbd79a54294b22acd Author: Konrad Windszus <[email protected]> AuthorDate: Sat May 9 17:42:19 2026 +0200 Remove empty SCR @Properties annotation after migration --- .../scripts/migrate_annotations.py | 40 +++++++++++++++++++++ .../scripts/tests/test_migrate_annotations.py | 42 ++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/skills/osgi-scr-migrator/scripts/migrate_annotations.py b/skills/osgi-scr-migrator/scripts/migrate_annotations.py index 70385722..03e3420f 100755 --- a/skills/osgi-scr-migrator/scripts/migrate_annotations.py +++ b/skills/osgi-scr-migrator/scripts/migrate_annotations.py @@ -246,6 +246,7 @@ class AnnotationMigrator: self._generate_metatype_config() self._update_imports() + self._cleanup_empty_properties_annotations() return '\n'.join(self.lines), self.changes_made except Exception as e: @@ -406,6 +407,7 @@ class AnnotationMigrator: 'org.apache.felix.scr.annotations.Deactivate': 'org.osgi.service.component.annotations.Deactivate', 'org.apache.felix.scr.annotations.Modified': 'org.osgi.service.component.annotations.Modified', 'org.apache.felix.scr.annotations.Property': None, + 'org.apache.felix.scr.annotations.Properties': None, 'org.apache.felix.scr.annotations.Reference': 'org.osgi.service.component.annotations.Reference', 'org.apache.felix.scr.annotations.ReferenceCardinality': 'org.osgi.service.component.annotations.ReferenceCardinality', 'org.apache.felix.scr.annotations.ReferencePolicy': 'org.osgi.service.component.annotations.ReferencePolicy', @@ -1067,6 +1069,44 @@ class AnnotationMigrator: self.lines.insert(insert_idx, f'import {new_import};') insert_idx += 1 + def _cleanup_empty_properties_annotations(self): + """Remove empty Felix SCR @Properties container annotations. + + This cleanup is run after @Property annotations are removed, so containers + that become empty do not remain in migrated code. + """ + i = 0 + while i < len(self.lines): + line = self.lines[i] + + if '@Properties' in line and not line.strip().startswith('//'): + annotation = line + paren_count = line.count('(') - line.count(')') + annotation_lines = [i] + j = i + 1 + + while paren_count > 0 and j < len(self.lines): + annotation += ' ' + self.lines[j].strip() + annotation_lines.append(j) + paren_count += self.lines[j].count('(') - self.lines[j].count(')') + j += 1 + + compact = re.sub(r'\s+', '', annotation) + empty_forms = { + '@Properties', + '@Properties()', + '@Properties({})', + '@Properties(value={})', + } + + if compact in empty_forms: + for idx in reversed(annotation_lines): + del self.lines[idx] + self.changes_made = True + continue + + i += 1 + def migrate_file(file_path: Path, dry_run: bool = False, stats: MigrationStats = None) -> bool: """Migrate a single Java file.""" diff --git a/skills/osgi-scr-migrator/scripts/tests/test_migrate_annotations.py b/skills/osgi-scr-migrator/scripts/tests/test_migrate_annotations.py index f15eb00d..7786e9ce 100644 --- a/skills/osgi-scr-migrator/scripts/tests/test_migrate_annotations.py +++ b/skills/osgi-scr-migrator/scripts/tests/test_migrate_annotations.py @@ -288,6 +288,48 @@ public class MyService { self.assertIn('org.osgi.service.component.propertytypes.ServiceVendor', new_content) self.assertIn('org.osgi.service.component.propertytypes.ServiceRanking', new_content) + def test_empty_properties_container_removed(self): + """Test that empty @Properties is removed after property migration.""" + content = '''package com.example; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Properties; +import org.apache.felix.scr.annotations.Property; + +@Component +@Properties({ + @Property(name = "service.vendor", value = "Apache Software Foundation") +}) +public class MyService { +} +''' + stats = MigrationStats() + migrator = AnnotationMigrator(content, Path("test.java"), stats) + new_content, changed = migrator.migrate() + + self.assertTrue(changed) + self.assertNotIn('@Properties', new_content) + self.assertNotIn('@Property', new_content) + + def test_preexisting_empty_properties_removed(self): + """Test that pre-existing empty @Properties annotation is cleaned up.""" + content = '''package com.example; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Properties; + +@Component +@Properties(value = { }) +public class MyService { +} +''' + stats = MigrationStats() + migrator = AnnotationMigrator(content, Path("test.java"), stats) + new_content, changed = migrator.migrate() + + self.assertTrue(changed) + self.assertNotIn('@Properties', new_content) + class TestReferenceMigration(unittest.TestCase): """Test @Reference migration."""
