diff --git a/ui/org.eclipse.pde.bnd.ui/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.bnd.ui/META-INF/MANIFEST.MF index 6e1de55ab63..fe0f58daa2f 100644 --- a/ui/org.eclipse.pde.bnd.ui/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.bnd.ui/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Vendor: Eclipse.org Bundle-Version: 1.2.300.qualifier Bundle-Localization: plugin Export-Package: org.eclipse.pde.bnd.ui.autocomplete;version="1.0.0";x-friends:="org.eclipse.pde.ui", + org.eclipse.pde.bnd.ui.model.resolution;version="1.0.0";x-friends:="org.eclipse.pde.ui", org.eclipse.pde.bnd.ui.plugins;x-internal:=true, org.eclipse.pde.bnd.ui.preferences;version="1.0.0";x-friends:="org.eclipse.pde.ui", org.eclipse.pde.bnd.ui.quickfix;version="1.0.0";x-friends:="org.eclipse.pde.ui", diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/BndBuilderCapReqLoader.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/BndBuilderCapReqLoader.java similarity index 80% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/BndBuilderCapReqLoader.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/BndBuilderCapReqLoader.java index bc57fbfc884..56a565c9758 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/BndBuilderCapReqLoader.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/BndBuilderCapReqLoader.java @@ -14,11 +14,9 @@ * Peter Kriens - ongoing enhancements * Christoph Rueger - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.model.resolution; import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.mapping; -import static java.util.stream.Collectors.toList; import java.io.File; import java.util.ArrayList; @@ -28,8 +26,9 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapper; +import org.eclipse.core.runtime.IProgressMonitor; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; @@ -45,8 +44,8 @@ public abstract class BndBuilderCapReqLoader implements CapReqLoader { protected final File file; - private Map> loadCapabilities; - private Map> loadRequirements; + private Map> loadCapabilities; + private Map> loadRequirements; public BndBuilderCapReqLoader(File file) { this.file = file; @@ -94,37 +93,33 @@ private void load() throws Exception { capabilities.addAll(resource.getCapabilities(null)); requirements.addAll(resource.getRequirements(null)); } - loadRequirements = requirements.stream() - .collect(groupingBy(Requirement::getNamespace, mapping(this::toRequirementWrapper, toList()))); + loadRequirements = requirements.stream().map(r -> toRequirementWrapper(r)) + .collect(groupingBy(Requirement::getNamespace, Collectors.toCollection(ArrayList::new))); loadCapabilities = capabilities.stream() - .collect(groupingBy(Capability::getNamespace, toList())); + .collect(groupingBy(Capability::getNamespace, Collectors.toCollection(ArrayList::new))); } @Override - public Map> loadCapabilities() throws Exception { + public CapReq loadCapReq(IProgressMonitor monitor) throws Exception { load(); - return loadCapabilities; + return new CapReq(loadCapabilities, loadRequirements); } - @Override - public Map> loadRequirements() throws Exception { - load(); - return loadRequirements; - } - - private RequirementWrapper toRequirementWrapper(Requirement req) { - RequirementWrapper rw = new RequirementWrapper(req); + private Requirement toRequirementWrapper(Requirement req) { if (req.getNamespace() .equals(PackageNamespace.PACKAGE_NAMESPACE)) { String pkgName = (String) req.getAttributes() .get(PackageNamespace.PACKAGE_NAMESPACE); try { - rw.requirers = findImportingClasses(pkgName); + List importingClasses = findImportingClasses(pkgName); + if (!importingClasses.isEmpty()) { + return new RequirementWithChildren(req, importingClasses); + } } catch (Exception e) { throw Exceptions.duck(e); } } - return rw; + return req; } private List findImportingClasses(String pkgName) throws Exception { @@ -172,4 +167,6 @@ public boolean equals(Object obj) { return Objects.equals(file, other.file); } + + } diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/BndFileCapReqLoader.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/BndFileCapReqLoader.java similarity index 98% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/BndFileCapReqLoader.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/BndFileCapReqLoader.java index 95be244be35..2b200552b1f 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/BndFileCapReqLoader.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/BndFileCapReqLoader.java @@ -13,7 +13,7 @@ * Sean Bright - ongoing enhancements * BJ Hargrave - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.model.resolution; import java.io.File; import java.io.IOException; diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReq.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReq.java new file mode 100644 index 00000000000..2ee1984621f --- /dev/null +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReq.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich project 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.bnd.ui.model.resolution; + +import java.util.Collection; +import java.util.Map; + +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; + +public record CapReq(Map> capabilities, + Map> requirements) { + +} diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/CapReqLoader.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqLoader.java similarity index 69% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/CapReqLoader.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqLoader.java index af9b3085ab1..d0d36348d5c 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/CapReqLoader.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqLoader.java @@ -12,23 +12,17 @@ * Neil Bartlett - initial API and implementation * BJ Hargrave - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.model.resolution; import java.io.Closeable; -import java.util.List; -import java.util.Map; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapper; -import org.osgi.resource.Capability; +import org.eclipse.core.runtime.IProgressMonitor; public interface CapReqLoader extends Closeable { String getShortLabel(); String getLongLabel(); - - Map> loadCapabilities() throws Exception; - - Map> loadRequirements() throws Exception; + CapReq loadCapReq(IProgressMonitor monitor) throws Exception; } diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/JarFileCapReqLoader.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/JarFileCapReqLoader.java similarity index 96% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/JarFileCapReqLoader.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/JarFileCapReqLoader.java index b3ec9a09b07..834a04c0a55 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/JarFileCapReqLoader.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/JarFileCapReqLoader.java @@ -12,7 +12,7 @@ * Neil Bartlett - initial API and implementation * BJ Hargrave - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.model.resolution; import java.io.File; import java.io.IOException; diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/ManifestCapReqLoader.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/ManifestCapReqLoader.java similarity index 79% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/ManifestCapReqLoader.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/ManifestCapReqLoader.java index 76360a1130f..b807212bcc1 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/ManifestCapReqLoader.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/ManifestCapReqLoader.java @@ -11,17 +11,14 @@ * Contributors: * Christoph Läubrich - initial API and implementation *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.model.resolution; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.util.List; -import java.util.Map; import java.util.jar.Manifest; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapper; -import org.osgi.resource.Capability; +import org.eclipse.core.runtime.IProgressMonitor; import aQute.bnd.osgi.resource.ResourceBuilder; @@ -56,14 +53,8 @@ public String getLongLabel() { } @Override - public Map> loadCapabilities() throws Exception { - loadManifest(); - return loadManifest().loadCapabilities(); - } - - @Override - public Map> loadRequirements() throws Exception { - return loadManifest().loadRequirements(); + public CapReq loadCapReq(IProgressMonitor monitor) throws Exception { + return loadManifest().loadCapReq(null); } private synchronized ResourceCapReqLoader loadManifest() throws IOException { diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWithChildren.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWithChildren.java new file mode 100644 index 00000000000..f7cc70768ec --- /dev/null +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWithChildren.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich project 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.bnd.ui.model.resolution; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.osgi.resource.Requirement; +import org.osgi.resource.Resource; + +public final class RequirementWithChildren implements Requirement { + + private Requirement req; + private Collection children; + + public RequirementWithChildren(Requirement req, Collection children) { + this.req = req; + this.children = children == null ? List.of() : List.copyOf(children); + } + + @Override + public String getNamespace() { + return req.getNamespace(); + } + + @Override + public Map getDirectives() { + return req.getDirectives(); + } + + @Override + public Map getAttributes() { + return req.getAttributes(); + } + + @Override + public Resource getResource() { + return req.getResource(); + } + + @Override + public boolean equals(Object obj) { + return req.equals(obj); + } + + @Override + public int hashCode() { + return req.hashCode(); + } + + public Collection getChildren() { + return children; + } + +} \ No newline at end of file diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/ResourceCapReqLoader.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/ResourceCapReqLoader.java similarity index 82% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/ResourceCapReqLoader.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/ResourceCapReqLoader.java index 2a362471d35..7386df4df6c 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/ResourceCapReqLoader.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/ResourceCapReqLoader.java @@ -14,19 +14,20 @@ * Peter Kriens - ongoing enhancements * Christoph Rueger - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.model.resolution; import java.io.IOException; import java.net.URI; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.pde.bnd.ui.ResourceUtils; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapper; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; @@ -62,8 +63,12 @@ public String getLongLabel() { } @Override - public Map> loadCapabilities() throws Exception { - Map> result = new HashMap<>(); + public CapReq loadCapReq(IProgressMonitor monitor) throws Exception { + return new CapReq(loadCapabilities(), loadRequirements()); + } + + private Map> loadCapabilities() throws Exception { + Map> result = new HashMap<>(); List caps = new ArrayList<>(resource.getCapabilities(null)); if (resource instanceof SupportingResource sr) { @@ -73,7 +78,7 @@ public Map> loadCapabilities() throws Exception { } for (Capability cap : caps) { String ns = cap.getNamespace(); - List listForNamespace = result.get(ns); + Collection listForNamespace = result.get(ns); if (listForNamespace == null) { listForNamespace = new LinkedList<>(); result.put(ns, listForNamespace); @@ -84,9 +89,8 @@ public Map> loadCapabilities() throws Exception { return result; } - @Override - public Map> loadRequirements() throws Exception { - Map> result = new HashMap<>(); + private Map> loadRequirements() throws Exception { + Map> result = new HashMap<>(); List reqs = new ArrayList<>(resource.getRequirements(null)); if (resource instanceof SupportingResource sr) { @@ -96,13 +100,12 @@ public Map> loadRequirements() throws Exception } for (Requirement req : reqs) { String ns = req.getNamespace(); - List listForNamespace = result.get(ns); + Collection listForNamespace = result.get(ns); if (listForNamespace == null) { listForNamespace = new LinkedList<>(); result.put(ns, listForNamespace); } - RequirementWrapper wrapper = new RequirementWrapper(req); - listForNamespace.add(wrapper); + listForNamespace.add(req); } return result; diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/AnalyseBundleResolutionJob.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/AnalyseBundleResolutionJob.java similarity index 51% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/AnalyseBundleResolutionJob.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/AnalyseBundleResolutionJob.java index a35ab4a000e..8cc1b894ee3 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/tasks/AnalyseBundleResolutionJob.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/AnalyseBundleResolutionJob.java @@ -16,87 +16,124 @@ * BJ Hargrave - ongoing enhancements * Sean Bright - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.tasks; +package org.eclipse.pde.bnd.ui.views.resolution; import static java.util.Collections.emptyList; -import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Stream; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ProgressMonitorWrapper; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapper; +import org.eclipse.pde.bnd.ui.model.resolution.CapReq; +import org.eclipse.pde.bnd.ui.model.resolution.CapReqLoader; import org.osgi.resource.Capability; import org.osgi.resource.Namespace; import aQute.bnd.build.model.EE; import aQute.bnd.osgi.resource.ResourceUtils; -public class AnalyseBundleResolutionJob extends Job { +class AnalyseBundleResolutionJob extends Job { - private final Set loaders; + private final Set loaders; - private Map> requirements; - private Map> capabilities; + private Map> requirements; + private Map> capabilities; private final EE ee; - public AnalyseBundleResolutionJob(String name, Set loaders) { - this(name, loaders, null); + private Consumer messageConsumer; + + public AnalyseBundleResolutionJob(String name, Set loaders, + Consumer messageConsumer) { + this(name, loaders, null, messageConsumer); } - public AnalyseBundleResolutionJob(String name, Set loaders, EE ee) { + public AnalyseBundleResolutionJob(String name, Set loaders, EE ee, + Consumer messageConsumer) { super(name); this.loaders = loaders; this.ee = ee; + this.messageConsumer = messageConsumer; } - private static void mergeMaps(Map> from, Map> into) { - for (Entry> entry : from.entrySet()) { + private static void mergeMaps(Map> from, Map> into) { + for (Entry> entry : from.entrySet()) { K key = entry.getKey(); + into.merge(key, entry.getValue(), (a, b) -> Stream.concat(a.stream(), b.stream()).distinct().toList()); + } + } - List list = into.get(key); - if (list == null) { - list = new ArrayList<>(); - into.put(key, list); - } - - list.addAll(entry.getValue()); + private static void mergeMapsWithMapping(Map> from, Map> into, + Function mapper) { + for (Entry> entry : from.entrySet()) { + K key = entry.getKey(); + into.merge(key, entry.getValue().stream().map(mapper).toList(), + (a, b) -> Stream.concat(a.stream(), b.stream()).distinct().toList()); } } @Override protected IStatus run(IProgressMonitor monitor) { + messageConsumer.accept("Working..."); try { - - // Load all the capabilities and requirements - Map> allCaps = new HashMap<>(); - Map> allReqs = new HashMap<>(); + Map> allCaps = new HashMap<>(); + Map> allReqs = new HashMap<>(); + SubMonitor convert = SubMonitor.convert(monitor, loaders.size()); for (CapReqLoader loader : loaders) { try (loader){ - Map> caps = loader.loadCapabilities(); - mergeMaps(caps, allCaps); + String baseLabel = "Loading " + loader.getShortLabel() ; + messageConsumer.accept(baseLabel + "..."); + CapReq loaded = loader.loadCapReq(new ProgressMonitorWrapper(convert.split(1)) { + String task; + @Override + public void beginTask(String name, int totalWork) { + task = baseLabel + " - " + name; + messageConsumer.accept(task); + super.beginTask(name, totalWork); + } + + @Override + public void setTaskName(String name) { + task = baseLabel + " - " + name; + super.setTaskName(name); + } - Map> reqs = loader.loadRequirements(); - mergeMaps(reqs, allReqs); + @Override + public void subTask(String name) { + if (task == null || task.isBlank()) { + messageConsumer.accept(baseLabel + " - " + name); + } else { + messageConsumer.accept(task + " (" + name + ")"); + } + super.subTask(name); + } + }); + mergeMaps(loaded.capabilities(), allCaps); + mergeMapsWithMapping(loaded.requirements(), allReqs, req -> new RequirementWrapper(req)); } catch (Exception e) { ILog.get().error("Error in Bnd resolution analysis.", e); } } - + messageConsumer.accept("Calculating..."); // Check for resolved requirements for (String namespace : allReqs.keySet()) { - List rws = allReqs.getOrDefault(namespace, emptyList()); - List candidates = allCaps.getOrDefault(namespace, emptyList()); + Collection rws = allReqs.getOrDefault(namespace, emptyList()); + Collection candidates = allCaps.getOrDefault(namespace, emptyList()); List javaCandidates = ee == null ? emptyList() : ee.getResource() @@ -125,13 +162,8 @@ protected IStatus run(IProgressMonitor monitor) { } // Generate the final results - // Set resultFiles = builderMap.keySet(); - // resultFileArray = resultFiles.toArray(new File[0]); - this.requirements = allReqs; this.capabilities = allCaps; - - // showResults(resultFileArray, importResults, exportResults); return Status.OK_STATUS; } catch (RuntimeException e) { throw e; @@ -140,11 +172,11 @@ protected IStatus run(IProgressMonitor monitor) { } } - public Map> getRequirements() { + public Map> getRequirements() { return Collections.unmodifiableMap(requirements); } - public Map> getCapabilities() { + public Map> getCapabilities() { return Collections.unmodifiableMap(capabilities); } } diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqComparator.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapReqComparator.java similarity index 87% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqComparator.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapReqComparator.java index a779b6503b8..f9328e8ab6a 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqComparator.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapReqComparator.java @@ -13,11 +13,12 @@ * BJ Hargrave - ongoing enhancements * Peter Kriens - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.model.resolution; +package org.eclipse.pde.bnd.ui.views.resolution; import java.util.Collection; import java.util.Comparator; +import org.eclipse.core.runtime.Adapters; import org.eclipse.pde.bnd.ui.model.resource.R5LabelFormatter; import org.osgi.framework.Version; import org.osgi.resource.Capability; @@ -29,14 +30,10 @@ public class CapReqComparator implements Comparator { @Override public int compare(Object o1, Object o2) { - if (o1 instanceof Requirement) { - return compareReqToObj((Requirement) o1, o2); + Requirement requirement = Adapters.adapt(o1, Requirement.class); + if (requirement != null) { + return compareReqToObj(requirement, o2); } - - if (o1 instanceof RequirementWrapper) { - return compareReqToObj(((RequirementWrapper) o1).requirement, o2); - } - if (o1 instanceof Capability) { return compareCapToObj((Capability) o1, o2); } @@ -45,14 +42,10 @@ public int compare(Object o1, Object o2) { } private int compareReqToObj(Requirement r1, Object o2) { - if (o2 instanceof Requirement) { - return compareReqToReq(r1, (Requirement) o2); + Requirement requirement = Adapters.adapt(o2, Requirement.class); + if (requirement != null) { + return compareReqToReq(r1, requirement); } - - if (o2 instanceof RequirementWrapper) { - return compareReqToReq(r1, ((RequirementWrapper) o2).requirement); - } - // requirements sort before other things return -1; } diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqMapContentProvider.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapReqMapContentProvider.java similarity index 90% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqMapContentProvider.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapReqMapContentProvider.java index b81a40c09f5..daae80c0959 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapReqMapContentProvider.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapReqMapContentProvider.java @@ -13,11 +13,10 @@ * BJ Hargrave - ongoing enhancements * Christoph Rueger - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.model.resolution; +package org.eclipse.pde.bnd.ui.views.resolution; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Comparator; import java.util.LinkedList; import java.util.List; @@ -25,8 +24,10 @@ import java.util.Map.Entry; import java.util.Set; +import org.eclipse.core.runtime.Adapters; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.pde.bnd.ui.model.resolution.RequirementWithChildren; import org.osgi.framework.namespace.BundleNamespace; import org.osgi.framework.namespace.HostNamespace; import org.osgi.framework.namespace.IdentityNamespace; @@ -111,25 +112,20 @@ public Object getParent(Object object) { @Override public boolean hasChildren(Object object) { - boolean children = false; - - if (object instanceof RequirementWrapper rw) { - children = rw.requirers != null && !rw.requirers.isEmpty(); + RequirementWithChildren rw = Adapters.adapt(object, RequirementWithChildren.class); + if (rw != null) { + return !rw.getChildren().isEmpty(); } - - return children; + return false; } @Override public Object[] getChildren(Object parent) { - Object[] result = EMPTY; - if (parent instanceof RequirementWrapper) { - Collection requirers = ((RequirementWrapper) parent).requirers; - if (requirers != null) { - result = requirers.toArray(); - } + RequirementWithChildren rw = Adapters.adapt(parent, RequirementWithChildren.class); + if (rw != null) { + return rw.getChildren().toArray(); } - return result; + return EMPTY; } public void setFilter(String filterString) { diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapabilityLabelProvider.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapabilityLabelProvider.java similarity index 98% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapabilityLabelProvider.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapabilityLabelProvider.java index 7d9daeec5a3..8b72cfc8287 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/CapabilityLabelProvider.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/CapabilityLabelProvider.java @@ -15,7 +15,7 @@ * Peter Kriens - ongoing enhancements * Christoph Rueger - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.model.resolution; +package org.eclipse.pde.bnd.ui.views.resolution; import java.util.Map.Entry; diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWrapper.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/RequirementWrapper.java similarity index 72% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWrapper.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/RequirementWrapper.java index 0d599652b46..082018bb79a 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWrapper.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/RequirementWrapper.java @@ -14,41 +14,27 @@ * Peter Kriens - ongoing enhancements * Christoph Rueger - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.model.resolution; +package org.eclipse.pde.bnd.ui.views.resolution; -import java.util.Collection; import java.util.Objects; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.pde.bnd.ui.model.resolution.RequirementWithChildren; import org.osgi.resource.Requirement; -import aQute.bnd.osgi.Constants; - -public class RequirementWrapper { +public class RequirementWrapper implements IAdaptable { public final Requirement requirement; public boolean resolved; public boolean java; - public Collection requirers; public RequirementWrapper(Requirement requirement) { this.requirement = requirement; } - public boolean isOptional() { - - String resolution = requirement.getDirectives() - .get(Constants.RESOLUTION); - - if (resolution == null) { - return false; - } - - return Constants.OPTIONAL.equals(resolution); - } - @Override public int hashCode() { - return Objects.hash(java, requirement, requirers, resolved); + return Objects.hash(java, requirement, resolved); } @Override @@ -64,7 +50,7 @@ public boolean equals(Object obj) { } RequirementWrapper other = (RequirementWrapper) obj; return java == other.java && Objects.equals(requirement, other.requirement) - && Objects.equals(requirers, other.requirers) && resolved == other.resolved; + && resolved == other.resolved; } @Override @@ -72,4 +58,15 @@ public String toString() { return "RequirementWrapper [resolved=" + resolved + ", java=" + java + ", requirement=" + requirement + "]"; } + @Override + public T getAdapter(Class adapter) { + if (adapter == Requirement.class) { + return adapter.cast(requirement); + } + if (adapter == RequirementWithChildren.class && requirement instanceof RequirementWithChildren) { + return adapter.cast(requirement); + } + return null; + } + } diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWrapperLabelProvider.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/RequirementWrapperLabelProvider.java similarity index 98% rename from ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWrapperLabelProvider.java rename to ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/RequirementWrapperLabelProvider.java index f2499987753..bf77610b4c8 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/resolution/RequirementWrapperLabelProvider.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/RequirementWrapperLabelProvider.java @@ -15,7 +15,7 @@ * Peter Kriens - ongoing enhancements * Christoph Rueger - ongoing enhancements *******************************************************************************/ -package org.eclipse.pde.bnd.ui.model.resolution; +package org.eclipse.pde.bnd.ui.views.resolution; import java.util.Map.Entry; diff --git a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/ResolutionView.java b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/ResolutionView.java index f986601d615..34ad1f6c22b 100644 --- a/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/ResolutionView.java +++ b/ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/resolution/ResolutionView.java @@ -19,6 +19,7 @@ import java.io.File; import java.text.MessageFormat; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -70,17 +71,12 @@ import org.eclipse.pde.bnd.ui.Resources; import org.eclipse.pde.bnd.ui.internal.PartAdapter; import org.eclipse.pde.bnd.ui.model.repo.ResourceProvider; -import org.eclipse.pde.bnd.ui.model.resolution.CapReqMapContentProvider; -import org.eclipse.pde.bnd.ui.model.resolution.CapabilityLabelProvider; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapper; -import org.eclipse.pde.bnd.ui.model.resolution.RequirementWrapperLabelProvider; -import org.eclipse.pde.bnd.ui.tasks.AnalyseBundleResolutionJob; -import org.eclipse.pde.bnd.ui.tasks.BndBuilderCapReqLoader; -import org.eclipse.pde.bnd.ui.tasks.BndFileCapReqLoader; -import org.eclipse.pde.bnd.ui.tasks.CapReqLoader; -import org.eclipse.pde.bnd.ui.tasks.JarFileCapReqLoader; -import org.eclipse.pde.bnd.ui.tasks.ManifestCapReqLoader; -import org.eclipse.pde.bnd.ui.tasks.ResourceCapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.BndBuilderCapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.BndFileCapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.CapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.JarFileCapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.ManifestCapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.ResourceCapReqLoader; import org.eclipse.pde.bnd.ui.views.ViewEventTopics; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; @@ -109,12 +105,12 @@ import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPartListener; import org.eclipse.ui.ISelectionListener; -import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.ResourceUtil; import org.eclipse.ui.part.ViewPart; +import org.osgi.framework.Constants; import org.osgi.framework.namespace.HostNamespace; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.resource.Capability; @@ -132,43 +128,42 @@ public class ResolutionView extends ViewPart implements ISelectionListener, IResourceChangeListener { - public static final String PLUGIN_ID = "bndtools.core"; + public static final String PLUGIN_ID = "bndtools.core"; - private final List ees = Arrays.asList(EE.values()); - private Display display = null; + private final List ees = Arrays.asList(EE.values()); + private Display display = null; - private Tree reqsTree = null; - private Table capsTable = null; + private Tree reqsTree = null; + private Table capsTable = null; - private TreeViewer reqsViewer; - private TableViewer capsViewer; + private TreeViewer reqsViewer; + private TableViewer capsViewer; - private Label reqsLabel; - private Label capsLabel; + private Label reqsLabel; + private Label capsLabel; - private ViewerFilter hideSelfImportsFilter; - private ViewerFilter hideOptionalRequirements; - private ViewerFilter filterShowCapsProblems; + private ViewerFilter hideSelfImportsFilter; + private ViewerFilter hideOptionalRequirements; + private ViewerFilter filterShowCapsProblems; - private boolean inputLocked = false; - private boolean outOfDate = false; - Set loaders; - private Job analysisJob; - private int currentEE = 4; + private boolean inputLocked = false; + private boolean outOfDate = false; + Set loaders; + private Job analysisJob; + private int currentEE = 4; - private final Set filteredCapabilityNamespaces; - private Set duplicateCapabilitiesWithDifferentHashes = new HashSet<>(); + private final Set filteredCapabilityNamespaces; + private Set duplicateCapabilitiesWithDifferentHashes = new HashSet<>(); private final FilterPanelPart reqsFilterPart = new FilterPanelPart(Resources.getScheduler()); private final FilterPanelPart capsFilterPart = new FilterPanelPart(Resources.getScheduler()); - private static final String SEARCHSTRING_HINT = "Enter search string (Space to separate terms; '*' for partial matches)"; + private static final String SEARCHSTRING_HINT = "Enter search string (Space to separate terms; '*' for partial matches)"; - private CapReqMapContentProvider reqsContentProvider; - private CapReqMapContentProvider capsContentProvider; + private CapReqMapContentProvider reqsContentProvider; + private CapReqMapContentProvider capsContentProvider; - private final IEventBroker eventBroker = PlatformUI.getWorkbench() - .getService(IEventBroker.class); + private final IEventBroker eventBroker = PlatformUI.getWorkbench().getService(IEventBroker.class); public ResolutionView() { filteredCapabilityNamespaces = Sets.of(IdentityNamespace.IDENTITY_NAMESPACE, HostNamespace.HOST_NAMESPACE); @@ -182,8 +177,16 @@ public void partActivated(IWorkbenchPart part) { if (outOfDate) { executeAnalysis(); } - } else if (part instanceof IEditorPart) { - IEditorInput editorInput = ((IEditorPart) part).getEditorInput(); + } else if (part instanceof IEditorPart editor) { + CapReqLoader editorLoader = Adapters.adapt(editor, CapReqLoader.class); + if (editorLoader != null) { + System.out.println("Got loader from editor!"); + setLoaders(Collections.singleton(editorLoader)); + return; + } + System.out.println("Editor is: " + editor); + IEditorInput editorInput = editor.getEditorInput(); + System.out.println("Editor input is: " + editorInput); IFile file = ResourceUtil.getFile(editorInput); if (file != null) { @@ -194,24 +197,17 @@ public void partActivated(IWorkbenchPart part) { if (loader != null) { setLoaders(Collections.singleton(loader)); - - if (getSite().getPage() - .isPartVisible(ResolutionView.this)) { - executeAnalysis(); - } else { - outOfDate = true; - } } } } + } else { + System.out.println("Activated: " + part); } } }; - - - private boolean setLoaders(Set newLoaders) { + System.out.println("Set loaders: " + newLoaders); Set oldLoaders = loaders; boolean swap = !oldLoaders.equals(newLoaders); if (swap) { @@ -220,6 +216,13 @@ private boolean setLoaders(Set newLoaders) { for (CapReqLoader l : swap ? oldLoaders : newLoaders) { IO.close(l); } + if (swap) { + if (getSite().getPage().isPartVisible(ResolutionView.this)) { + executeAnalysis(); + } else { + outOfDate = true; + } + } return swap; } @@ -272,9 +275,10 @@ public void createPartControl(Composite parent) { reqsViewer.addDoubleClickListener(event -> handleReqsViewerDoubleClickEvent(event)); reqsViewer.getControl() - .addKeyListener(createCopyToClipboardAdapter(reqsViewer, - (IStructuredSelection selection, StringBuilder clipboardContent) -> reqsCopyToClipboard(selection, - (RequirementWrapperLabelProvider) reqsViewer.getLabelProvider(), clipboardContent))); + .addKeyListener(createCopyToClipboardAdapter(reqsViewer, + (IStructuredSelection selection, StringBuilder clipboardContent) -> reqsCopyToClipboard( + selection, (RequirementWrapperLabelProvider) reqsViewer.getLabelProvider(), + clipboardContent))); Composite capsPanel = new Composite(splitPanel, SWT.NONE); capsPanel.setBackground(parent.getBackground()); @@ -323,9 +327,10 @@ public boolean select(Viewer viewer, Object parentElement, Object element) { capsViewer.addDoubleClickListener(event -> handleCapsViewerDoubleClickEvent(event)); capsViewer.getTable() - .addKeyListener(createCopyToClipboardAdapter(capsViewer, - (IStructuredSelection selection1, StringBuilder clipboardContent1) -> capsCopyToClipboard(selection1, - (CapabilityLabelProvider) capsViewer.getLabelProvider(), clipboardContent1))); + .addKeyListener(createCopyToClipboardAdapter(capsViewer, + (IStructuredSelection selection1, StringBuilder clipboardContent1) -> capsCopyToClipboard( + selection1, (CapabilityLabelProvider) capsViewer.getLabelProvider(), + clipboardContent1))); hideSelfImportsFilter = new ViewerFilter() { @@ -343,47 +348,35 @@ public boolean select(Viewer viewer, Object parentElement, Object element) { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { - if (element instanceof RequirementWrapper rw) { - return !rw.isOptional(); + Requirement requirement = Adapters.adapt(element, Requirement.class); + if (requirement != null) { + String resolution = requirement.getDirectives().get(Constants.RESOLUTION_DIRECTIVE); + return !Constants.RESOLUTION_OPTIONAL.equals(resolution); } return true; } }; + reqsViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, + new Transfer[] { LocalSelectionTransfer.getTransfer() }, new LocalTransferDragListener(reqsViewer)); - reqsViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] { - LocalSelectionTransfer.getTransfer() - }, new LocalTransferDragListener(reqsViewer)); - - capsViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] { - LocalSelectionTransfer.getTransfer() - }, new LocalTransferDragListener(capsViewer)); + capsViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, + new Transfer[] { LocalSelectionTransfer.getTransfer() }, new LocalTransferDragListener(capsViewer)); reqsViewer.addOpenListener(this::openEditor); fillActionBars(); - getSite().getPage() - .addPostSelectionListener(this); - getSite().getPage() - .addPartListener(partAdapter); - ResourcesPlugin.getWorkspace() - .addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); + getSite().getPage().addPostSelectionListener(this); + getSite().getPage().addPartListener(partAdapter); + ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); // Current selection & part - IWorkbenchPart activePart = getSite().getPage() - .getActivePart(); - ISelection activeSelection = getSite().getWorkbenchWindow() - .getSelectionService() - .getSelection(); + IWorkbenchPart activePart = getSite().getPage().getActivePart(); + ISelection activeSelection = getSite().getWorkbenchWindow().getSelectionService().getSelection(); selectionChanged(activePart, activeSelection); } - - - - - private void openEditor(OpenEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); for (Iterator iter = selection.iterator(); iter.hasNext();) { @@ -392,8 +385,7 @@ private void openEditor(OpenEvent event) { String className = clazz.getFQN(); IType type = null; if (!loaders.isEmpty()) { - IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace() - .getRoot(); + IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace().getRoot(); for (CapReqLoader loader : loaders) { if (loader instanceof BndBuilderCapReqLoader) { File loaderFile = ((BndBuilderCapReqLoader) loader).getFile(); @@ -407,8 +399,9 @@ private void openEditor(OpenEvent event) { } } catch (JavaModelException e1) { ErrorDialog.openError(getSite().getShell(), "Error", "", - new Status(IStatus.ERROR, PLUGIN_ID, 0, - MessageFormat.format("Error opening Java class '{0}'.", className), e1)); + new Status(IStatus.ERROR, PLUGIN_ID, 0, + MessageFormat.format("Error opening Java class '{0}'.", className), + e1)); } } } @@ -420,19 +413,18 @@ private void openEditor(OpenEvent event) { JavaUI.openInEditor(type, true, true); } } catch (PartInitException e2) { - ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, PLUGIN_ID, - 0, MessageFormat.format("Error opening Java editor for class '{0}'.", className), e2)); + ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, PLUGIN_ID, 0, + MessageFormat.format("Error opening Java editor for class '{0}'.", className), e2)); } catch (JavaModelException e3) { - ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, PLUGIN_ID, - 0, MessageFormat.format("Error opening Java class '{0}'.", className), e3)); + ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, PLUGIN_ID, 0, + MessageFormat.format("Error opening Java class '{0}'.", className), e3)); } } } } void fillActionBars() { - IToolBarManager toolBarManager = getViewSite().getActionBars() - .getToolBarManager(); + IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager(); // Reqs Buttons toolBarManager.add(createToggleHideSelfImportsButton()); @@ -450,10 +442,6 @@ void fillActionBars() { } - - - - private void doEEActionMenu(IToolBarManager toolBarManager) { MenuManager menuManager = new MenuManager("Java", "resolutionview.java.menu"); @@ -465,9 +453,7 @@ public void runWithEvent(Event event) { if (items != null && items.length == ees.size()) { menu.setDefaultItem(items[currentEE]); } - Point location = getViewSite().getShell() - .getDisplay() - .getCursorLocation(); + Point location = getViewSite().getShell().getDisplay().getCursorLocation(); menu.setLocation(location.x, location.y); menu.setVisible(true); } @@ -510,23 +496,21 @@ protected void setEE(int ee) { } @Override - public void setFocus() {} + public void setFocus() { + } @Override public void dispose() { - getSite().getPage() - .removeSelectionListener(this); - ResourcesPlugin.getWorkspace() - .removeResourceChangeListener(this); - getSite().getPage() - .removePartListener(partAdapter); - setLoaders(Collections. emptySet()); + getSite().getPage().removeSelectionListener(this); + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + getSite().getPage().removePartListener(partAdapter); + setLoaders(Collections.emptySet()); duplicateCapabilitiesWithDifferentHashes.clear(); super.dispose(); } - public void setInput(Set sourceLoaders, Map> capabilities, - Map> requirements) { + private void setInput(Set sourceLoaders, Map> capabilities, + Map> requirements) { setLoaders(sourceLoaders); sourceLoaders = loaders; if (reqsTree != null && !reqsTree.isDisposed() && capsTable != null && !capsTable.isDisposed()) { @@ -551,13 +535,10 @@ public void setInput(Set sourceLoaders, Map caps = capabilities.values() - .stream() - .flatMap(List::stream) - .toList(); + List caps = capabilities.values().stream().flatMap(Collection::stream).toList(); duplicateCapabilitiesWithDifferentHashes = new HashSet( - ResourceUtils.detectDuplicateCapabilitiesWithDifferentHashes("osgi.wiring.package", caps)); + ResourceUtils.detectDuplicateCapabilitiesWithDifferentHashes("osgi.wiring.package", caps)); updateCapsLabel(); @@ -566,27 +547,15 @@ public void setInput(Set sourceLoaders, Map loaders = getLoadersFromSelection((IStructuredSelection) selection); - if (setLoaders(loaders)) { - IWorkbenchPage page = getSite().getPage(); - if (page != null && page.isPartVisible(this)) { - executeAnalysis(); - } else { - outOfDate = true; - } - } - + setLoaders(getLoadersFromSelection((IStructuredSelection) selection)); } private void updateReqsLabel() { - reqsLabel.setText("Requirements: " + reqsViewer.getTree() - .getItemCount()); - reqsLabel.getParent() - .layout(); + reqsLabel.setText("Requirements: " + reqsViewer.getTree().getItemCount()); + reqsLabel.getParent().layout(); } private void updateCapsLabel() { @@ -594,8 +563,7 @@ private void updateCapsLabel() { if (!duplicateCapabilitiesWithDifferentHashes.isEmpty()) { int problemCount = 0; - TableItem[] items = capsViewer.getTable() - .getItems(); + TableItem[] items = capsViewer.getTable().getItems(); for (int i = 0; i < items.length; i++) { TableItem tableItem = items[i]; Object data = tableItem.getData(); @@ -610,11 +578,9 @@ private void updateCapsLabel() { problemAddition = " Problems: " + problemCount; } } - capsLabel.setText("Capabilities: " + capsViewer.getTable() - .getItemCount() + problemAddition); + capsLabel.setText("Capabilities: " + capsViewer.getTable().getItemCount() + problemAddition); - capsLabel.getParent() - .layout(); + capsLabel.getParent().layout(); } private Set getLoadersFromSelection(IStructuredSelection structSel) { @@ -683,20 +649,16 @@ void executeAnalysis() { if (!loaders.isEmpty()) { final AnalyseBundleResolutionJob job = new AnalyseBundleResolutionJob("importExportAnalysis", loaders, - ees.get(currentEE)); + ees.get(currentEE), msg -> { + if (display != null && !display.isDisposed()) { + display.execute(() -> setContentDescription(msg)); + } + }); job.setSystem(true); job.addJobChangeListener(new JobChangeAdapter() { @Override public void aboutToRun(IJobChangeEvent event) { - if (display != null && !display.isDisposed()) { - Runnable update = () -> setContentDescription("Working..."); - if (display.getThread() == Thread.currentThread()) { - update.run(); - } else { - display.asyncExec(update); - } - } } @Override @@ -704,8 +666,8 @@ public void done(IJobChangeEvent event) { IStatus result = job.getResult(); if (result != null && result.isOK()) { if (display != null && !display.isDisposed()) { - display - .asyncExec(() -> setInput(loaders, job.getCapabilities(), job.getRequirements())); + display.asyncExec( + () -> setInput(loaders, job.getCapabilities(), job.getRequirements())); } } } @@ -722,15 +684,13 @@ public void done(IJobChangeEvent event) { @Override public void resourceChanged(IResourceChangeEvent event) { if (!loaders.isEmpty()) { - IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace() - .getRoot(); + IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace().getRoot(); for (CapReqLoader loader : loaders) { if (loader instanceof BndBuilderCapReqLoader) { File file = ((BndBuilderCapReqLoader) loader).getFile(); IFile[] wsfiles = wsroot.findFilesForLocationURI(file.toURI()); for (IFile wsfile : wsfiles) { - if (event.getDelta() - .findMember(wsfile.getFullPath()) != null) { + if (event.getDelta().findMember(wsfile.getFullPath()) != null) { executeAnalysis(); break; } @@ -749,7 +709,8 @@ public LocalTransferDragListener(Viewer viewer) { } @Override - public void dragStart(DragSourceEvent event) {} + public void dragStart(DragSourceEvent event) { + } @Override public void dragSetData(DragSourceEvent event) { @@ -760,12 +721,12 @@ public void dragSetData(DragSourceEvent event) { } @Override - public void dragFinished(DragSourceEvent event) {} + public void dragFinished(DragSourceEvent event) { + } } private void handleReqsViewerDoubleClickEvent(DoubleClickEvent event) { - if (!event.getSelection() - .isEmpty()) { + if (!event.getSelection().isEmpty()) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); final Object element = selection.getFirstElement(); @@ -780,8 +741,7 @@ private void handleReqsViewerDoubleClickEvent(DoubleClickEvent event) { } private void handleCapsViewerDoubleClickEvent(DoubleClickEvent event) { - if (!event.getSelection() - .isEmpty()) { + if (!event.getSelection().isEmpty()) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); final Object element = selection.getFirstElement(); @@ -793,13 +753,12 @@ private void handleCapsViewerDoubleClickEvent(DoubleClickEvent event) { // (&(osgi.wiring.package=my.package.foo)(version>=1.7.23)) Requirement req = CapReqBuilder.createRequirementFromCapability(cap, (name) -> { if (name.equals("bundle-symbolic-name") || name.equals("bundle-version") - || name.equals("bnd.hashes")) { + || name.equals("bnd.hashes")) { return false; } return true; - }) - .buildSyntheticRequirement(); + }).buildSyntheticRequirement(); // Open AdvanvedSearch of RepositoriesView eventBroker.post(ViewEventTopics.REPOSITORIESVIEW_OPEN_ADVANCED_SEARCH.topic(), req); } @@ -810,13 +769,13 @@ private void handleCapsViewerDoubleClickEvent(DoubleClickEvent event) { /** * Generic copy to clipboard handling via Ctrl+C or MacOS: Cmd+C * - * @param viewer the viewer - * @param clipboardContentExtractor handler to extract content from the - * selected items. + * @param viewer the viewer + * @param clipboardContentExtractor handler to extract content from the selected + * items. * @return a KeyAdapter copying content of the selected items to clipboard */ private KeyAdapter createCopyToClipboardAdapter(StructuredViewer viewer, - BiConsumer clipboardContentExtractor) { + BiConsumer clipboardContentExtractor) { return new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { @@ -830,11 +789,8 @@ public void keyPressed(KeyEvent e) { if (clipboardString.length() > 0) { Clipboard clipboard = new Clipboard(Display.getCurrent()); TextTransfer textTransfer = TextTransfer.getInstance(); - clipboard.setContents(new Object[] { - clipboardString.toString() - }, new Transfer[] { - textTransfer - }); + clipboard.setContents(new Object[] { clipboardString.toString() }, + new Transfer[] { textTransfer }); clipboard.dispose(); } } @@ -843,9 +799,8 @@ public void keyPressed(KeyEvent e) { }; } - private void reqsCopyToClipboard(IStructuredSelection selection, RequirementWrapperLabelProvider lp, - StringBuilder clipboardContent) { + StringBuilder clipboardContent) { for (Iterator iterator = selection.iterator(); iterator.hasNext();) { Object element = iterator.next(); @@ -869,10 +824,8 @@ private void reqsCopyToClipboard(IStructuredSelection selection, RequirementWrap } } - - private void capsCopyToClipboard(IStructuredSelection selection, CapabilityLabelProvider lp, - StringBuilder clipboardContent) { + StringBuilder clipboardContent) { for (Iterator iterator = selection.iterator(); iterator.hasNext();) { Object element = iterator.next(); @@ -919,7 +872,7 @@ public void runWithEvent(Event event) { if (isChecked()) { capsViewer.addFilter(filterShowCapsProblems); this.setToolTipText( - "Showing capabilities containing packages that have the same name but differ in the contained classes."); + "Showing capabilities containing packages that have the same name but differ in the contained classes."); } else { capsViewer.removeFilter(filterShowCapsProblems); this.setToolTipText(tooltipTextUnlocked); @@ -930,8 +883,7 @@ public void runWithEvent(Event event) { }; toggleShowProblemCaps.setChecked(false); toggleShowProblemCaps.setImageDescriptor(Resources.getImageDescriptor("/icons/warning_obj.svg")); - toggleShowProblemCaps - .setToolTipText(tooltipTextUnlocked); + toggleShowProblemCaps.setToolTipText(tooltipTextUnlocked); return toggleShowProblemCaps; } @@ -959,7 +911,7 @@ public void runWithEvent(Event event) { private IAction createToggleHideSelfImportsButton() { String toolTipTextShowAll = "Showing all requirements."; String toolTipTextHideSelfImports = "Hiding resolved (including self-imported) requirements.\n\n" - + "Requirements that are resolved (exported and imported) within the set of selected bundles are hidden. Click to show all requirements."; + + "Requirements that are resolved (exported and imported) within the set of selected bundles are hidden. Click to show all requirements."; IAction toggleShowSelfImports = new Action("showSelfImports", IAction.AS_CHECK_BOX) { @Override @@ -997,7 +949,8 @@ public void runWithEvent(Event event) { } }; toggleShowShowUnresolvedReqsFilter.setChecked(false); - toggleShowShowUnresolvedReqsFilter.setImageDescriptor(Resources.getImageDescriptor("/icons/excludeMode_filter.svg")); + toggleShowShowUnresolvedReqsFilter + .setImageDescriptor(Resources.getImageDescriptor("/icons/excludeMode_filter.svg")); toggleShowShowUnresolvedReqsFilter.setToolTipText(toggleShowShowUnresolvedReqsFilterUnchecked); return toggleShowShowUnresolvedReqsFilter; } diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetCapReqLoader.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetCapReqLoader.java new file mode 100644 index 00000000000..ffcf2266294 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetCapReqLoader.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2025 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.internal.ui.editor.targetdefinition; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.equinox.frameworkadmin.BundleInfo; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.pde.bnd.ui.model.resolution.CapReq; +import org.eclipse.pde.bnd.ui.model.resolution.CapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.JarFileCapReqLoader; +import org.eclipse.pde.bnd.ui.model.resolution.ResourceCapReqLoader; +import org.eclipse.pde.core.target.ITargetDefinition; +import org.eclipse.pde.core.target.TargetBundle; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IPathEditorInput; +import org.osgi.framework.BundleException; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; + +import aQute.bnd.osgi.Processor; +import aQute.bnd.osgi.resource.ResourceBuilder; + +class TargetCapReqLoader implements CapReqLoader { + + private ITargetDefinition target; + private IEditorInput editorInput; + + public TargetCapReqLoader(ITargetDefinition target, IEditorInput editorInput) { + this.target = target; + this.editorInput = editorInput; + } + + @Override + public void close() throws IOException { + // nothing to do + } + + @Override + public String getShortLabel() { + return String.format("Target Platform '%s'", target.getName()); //$NON-NLS-1$ + } + + @Override + public String getLongLabel() { + if (editorInput instanceof IPathEditorInput path) { + return String.format("%s (%s)", getShortLabel(), path.getPath()); //$NON-NLS-1$ + } + return getShortLabel(); + } + + @Override + public CapReq loadCapReq(IProgressMonitor monitor) throws Exception { + TargetBundle[] bundles = target.getBundles(); + if (bundles == null || bundles.length == 0) { + // TODO need a way to trigger a reload when target changes! + return new CapReq(Map.of(), Map.of()); + } + Map> capabilityResults = new LinkedHashMap<>(); + Map> requirementResults = new LinkedHashMap<>(); + monitor.beginTask("read bundles", bundles.length); //$NON-NLS-1$ + for (int i = 0; i < bundles.length; i++) { + TargetBundle targetBundle = bundles[i]; + if (targetBundle.isSourceBundle()) { + continue; + } + monitor.subTask((i + 1) + " / " + bundles.length); //$NON-NLS-1$ + CapReqLoader loader = toLoader(targetBundle); + if (loader != null) { + CapReq result = loader.loadCapReq(null); + mergeMaps(result.capabilities(), capabilityResults); + mergeMaps(result.requirements(), requirementResults); + } + } + return new CapReq(capabilityResults, requirementResults); + } + + private CapReqLoader toLoader(TargetBundle targetBundle) throws IOException, BundleException { + BundleInfo info = targetBundle.getBundleInfo(); + URI location = info.getLocation(); + if (location != null && "file".equals(location.getScheme())) { //$NON-NLS-1$ + File jarFile = new File(location); + if (jarFile.isFile()) { + return new JarFileCapReqLoader(jarFile); + } + } + String manifest = info.getManifest(); + if (manifest != null && !manifest.isBlank()) { + // TODO use equinox manifest reader + ResourceBuilder builder = new ResourceBuilder(); + Map bundleManifest; + try (ByteArrayInputStream stream = new ByteArrayInputStream(manifest.getBytes(StandardCharsets.UTF_8))) { + bundleManifest = ManifestElement.parseBundleManifest(stream); + } + Processor processor = new Processor(); + processor.addProperties(bundleManifest); + builder.addManifest(processor); + return new ResourceCapReqLoader(builder.build()); + } + return null; + } + + private static void mergeMaps(Map> from, Map> into) { + for (Entry> entry : from.entrySet()) { + K key = entry.getKey(); + into.merge(key, entry.getValue(), (a, b) -> Stream.concat(a.stream(), b.stream()).distinct().toList()); + } + } + + @Override + public int hashCode() { + return Objects.hash(editorInput, target); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TargetCapReqLoader other = (TargetCapReqLoader) obj; + return Objects.equals(editorInput, other.editorInput) && Objects.equals(target, other.target); + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java index 36127312f3b..2a1f548a645 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java @@ -55,6 +55,7 @@ import org.eclipse.jface.text.source.ISourceViewerExtension5; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.bnd.ui.model.resolution.CapReqLoader; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.ITargetHandle; import org.eclipse.pde.core.target.ITargetPlatformService; @@ -300,6 +301,9 @@ public T getAdapter(Class adapter) { return adapter.cast(target.getHandle()); } } + if (adapter == CapReqLoader.class) { + return adapter.cast(new TargetCapReqLoader(getTarget(), getEditorInput())); + } return super.getAdapter(adapter); } /**