From 4314d005e06c3308cb92a89a6b5433b83acc47ee Mon Sep 17 00:00:00 2001 From: Christoph Rueger Date: Thu, 12 Sep 2024 12:32:28 +0200 Subject: [PATCH] RepoView: Clipboard: Bundles with self-imports For debugging purposes it is useful to identify bundles containing self-imported packages. I added this to the Repo-Browser on Right Click on a Repo / Copy to Clipboard / Bundles with Self-imports. It outputs a simple list to the clipboard which can be pased to a texteditor for further analysis. In the future this might be refactored into more sophisticated solution but for the moment it is already useful. Signed-off-by: Christoph Rueger --- .../bnd/osgi/resource/ResourceUtils.java | 56 +++++++++++++++++++ .../views/repository/RepositoriesView.java | 32 +++++++++++ 2 files changed, 88 insertions(+) diff --git a/biz.aQute.bndlib/src/aQute/bnd/osgi/resource/ResourceUtils.java b/biz.aQute.bndlib/src/aQute/bnd/osgi/resource/ResourceUtils.java index 38a1badd2a..c6910e2b60 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/osgi/resource/ResourceUtils.java +++ b/biz.aQute.bndlib/src/aQute/bnd/osgi/resource/ResourceUtils.java @@ -24,6 +24,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -97,6 +98,10 @@ public abstract class ResourceUtils { private static final Converter cnv = new Converter() .hook(Version.class, (dest, o) -> toVersion(o)); + private static final String PACKAGE_FILTER_PATTERN = "osgi.wiring.package=([^)]*)"; + private static final Pattern pkgFilterPattern = Pattern + .compile(PACKAGE_FILTER_PATTERN); + public interface IdentityCapability extends Capability { public enum Type { bundle(IdentityNamespace.TYPE_BUNDLE), @@ -952,4 +957,55 @@ public static List detectDuplicateCapabilitiesWithDifferentHashes(St return culprits; } + + /** + * Calculates a list of package names which are exported and also imported. + * This can be handy for debugging and identify unwanted self-imports. + * + * @param r the resource + * @return a non-null list of self-import package names. + */ + public static Set getSelfImportPackages(Resource r) { + List caps = r.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE); + List requirements = r.getRequirements(PackageNamespace.PACKAGE_NAMESPACE); + + Set exportedPackages = new LinkedHashSet<>(); + Set importedPackages = new LinkedHashSet<>(); + + // Populate exported packages + for (Capability cap : caps) { + String packageName = (String) cap.getAttributes() + .get(PackageNamespace.PACKAGE_NAMESPACE); + if (packageName != null) { + exportedPackages.add(packageName); + } + } + + // Populate imported packages + for (Requirement req : requirements) { + String requirementPackage = getRequirementPackage(req); + if (requirementPackage != null) { + importedPackages.add(requirementPackage); + } + } + + Set selfImports = new LinkedHashSet<>(exportedPackages); + selfImports.retainAll(importedPackages); + + return selfImports; + } + + private static String getRequirementPackage(Requirement req) { + + String filter = req.getDirectives() + .get(Namespace.REQUIREMENT_FILTER_DIRECTIVE); + if (filter != null) { + Matcher m = pkgFilterPattern.matcher(filter); + if (m.find()) { + return m.group(1); + } + } + + return null; + } } diff --git a/bndtools.core/src/bndtools/views/repository/RepositoriesView.java b/bndtools.core/src/bndtools/views/repository/RepositoriesView.java index 41530690fd..0298511889 100644 --- a/bndtools.core/src/bndtools/views/repository/RepositoriesView.java +++ b/bndtools.core/src/bndtools/views/repository/RepositoriesView.java @@ -96,6 +96,7 @@ import aQute.bnd.build.Workspace; import aQute.bnd.exceptions.Exceptions; import aQute.bnd.http.HttpClient; +import aQute.bnd.osgi.resource.ResourceUtils; import aQute.bnd.service.Actionable; import aQute.bnd.service.Refreshable; import aQute.bnd.service.Registry; @@ -114,6 +115,7 @@ import bndtools.model.repo.RepositoryBundleVersion; import bndtools.model.repo.RepositoryEntry; import bndtools.model.repo.RepositoryTreeLabelProvider; +import bndtools.model.repo.ResourceProvider; import bndtools.model.repo.SearchableRepositoryTreeContentProvider; import bndtools.preferences.BndPreferences; import bndtools.preferences.WorkspaceOfflineChangeAdapter; @@ -1127,6 +1129,7 @@ private void addCopyToClipboardSubMenueEntries(Actionable act, final RepositoryP if ((act instanceof Repository) || (act instanceof RepositoryPlugin)) { hmenu.add(createContextMenueCopyInfoRepo(act, rp, clipboard)); + hmenu.add(createContextMenueCopyBundlesWithSelfImports(act, rp, clipboard)); } } @@ -1154,6 +1157,35 @@ private HierarchicalLabel createContextMenueCopyInfoRepo(Actionable act, })); } + private HierarchicalLabel createContextMenueCopyBundlesWithSelfImports(Actionable act, final RepositoryPlugin rp, + final Clipboard clipboard) { + return new HierarchicalLabel("Copy to clipboard :: Bundles with self-imports", (label) -> createAction(label.getLeaf(), + "Add list of bundles containing self-imported packages in their Manifest.", true, false, rp, () -> { + + final StringBuilder sb = new StringBuilder(); + + Object[] result = contentProvider.getChildren(rp); + for (Object object : result) { + if (object instanceof ResourceProvider prov) { + org.osgi.resource.Resource r = prov.getResource(); + + Set selfImports = ResourceUtils.getSelfImportPackages(r); + if (!selfImports.isEmpty()) { + sb.append(r.toString() + " has " + selfImports.size() + " self-import packages: " + + selfImports + "\n"); + } + } + } + + if (sb.isEmpty()) { + clipboard.copy("-Empty-"); + } else { + clipboard.copy(sb.toString()); + } + + })); + } + private HierarchicalLabel createContextMenueCopyInfoRepoBundle(Actionable act, final RepositoryPlugin rp, final Clipboard clipboard, RepositoryBundle rb) { return new HierarchicalLabel("Copy to clipboard :: Copy info", (label) -> createAction(label.getLeaf(),