Skip to content

Commit cc83723

Browse files
committed
[TP-Editor] Propose children of composite repositories
1 parent 52e96a6 commit cc83723

File tree

3 files changed

+55
-17
lines changed

3 files changed

+55
-17
lines changed

ui/org.eclipse.pde.genericeditor.extension/src/org/eclipse/pde/internal/genericeditor/target/extension/autocomplete/processors/AttributeValueCompletionProcessor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*******************************************************************************/
1616
package org.eclipse.pde.internal.genericeditor.target.extension.autocomplete.processors;
1717

18+
import java.net.URI;
1819
import java.util.List;
1920
import java.util.Map;
2021
import java.util.stream.Stream;
@@ -111,6 +112,11 @@ public ICompletionProposal[] getCompletionProposals() {
111112
}
112113
}
113114

115+
if (ITargetConstants.REPOSITORY_LOCATION_ATTR.equalsIgnoreCase(acKey)) {
116+
List<URI> children = RepositoryCache.fetchChildrenOfRepo(searchTerm);
117+
return toProposals(children.stream().map(URI::toString));
118+
}
119+
114120
return new ICompletionProposal[] {};
115121
}
116122

ui/org.eclipse.pde.genericeditor.extension/src/org/eclipse/pde/internal/genericeditor/target/extension/model/RepositoryCache.java

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,18 @@
2424
import java.util.concurrent.CompletableFuture;
2525
import java.util.concurrent.ConcurrentHashMap;
2626
import java.util.concurrent.Future;
27+
import java.util.function.Function;
2728
import java.util.stream.Collectors;
2829
import java.util.stream.Stream;
2930

3031
import org.eclipse.core.runtime.ILog;
3132
import org.eclipse.core.runtime.jobs.Job;
3233
import org.eclipse.equinox.p2.metadata.IVersionedId;
34+
import org.eclipse.equinox.p2.metadata.VersionedId;
3335
import org.eclipse.osgi.util.NLS;
3436
import org.eclipse.pde.internal.genericeditor.target.extension.p2.Messages;
3537
import org.eclipse.pde.internal.genericeditor.target.extension.p2.P2Fetcher;
38+
import org.eclipse.pde.internal.genericeditor.target.extension.p2.P2Fetcher.RepositoryContent;
3639

3740
/**
3841
* This class is used to cache the p2 repositories completion information order
@@ -48,7 +51,10 @@ private RepositoryCache() {
4851
// avoid instantiation
4952
}
5053

51-
private static final Map<URI, CompletableFuture<Map<String, List<IVersionedId>>>> CACHE = new ConcurrentHashMap<>();
54+
private static record RepositoryMetadata(Map<String, List<IVersionedId>> units, List<URI> children) {
55+
}
56+
57+
private static final Map<URI, CompletableFuture<RepositoryMetadata>> CACHE = new ConcurrentHashMap<>();
5258

5359
/**
5460
* Fetches information and caches it.
@@ -64,36 +70,43 @@ private RepositoryCache() {
6470
*/
6571
public static Map<String, List<IVersionedId>> fetchP2UnitsFromRepos(List<String> repositories) {
6672
if (repositories.size() == 1) {
67-
return getFutureValue(fetchP2DataOfRepo(repositories.get(0)));
73+
return getFutureValue(fetchP2DataOfRepo(repositories.get(0)), RepositoryMetadata::units, Map.of());
6874
}
6975
var repos = repositories.stream().map(RepositoryCache::fetchP2DataOfRepo).toList();
7076
// Fetch all repos at once to await pending metadata in parallel
71-
return toSortedMap(repos.stream().map(RepositoryCache::getFutureValue) //
77+
return toSortedMap(repos.stream()
78+
.map(r -> getFutureValue(r, RepositoryMetadata::units, Map.<String, List<IVersionedId>>of()))
7279
.map(Map::values).flatMap(Collection::stream).flatMap(List::stream));
7380
}
7481

82+
public static List<URI> fetchChildrenOfRepo(String repository) {
83+
return getFutureValue(fetchP2DataOfRepo(repository), RepositoryMetadata::children, List.of());
84+
}
85+
7586
public static void prefetchP2MetadataOfRepository(String repository) {
7687
fetchP2DataOfRepo(repository);
7788
}
7889

79-
private static Future<Map<String, List<IVersionedId>>> fetchP2DataOfRepo(String repository) {
90+
private static Future<RepositoryMetadata> fetchP2DataOfRepo(String repository) {
8091
URI location;
81-
try {
82-
location = new URI(repository);
92+
try { // always have a trailing slash to avoid duplicated cache entries
93+
location = new URI(repository + (repository.endsWith("/") ? "" : "/"));
8394
} catch (URISyntaxException e) {
8495
return CompletableFuture.failedFuture(e);
8596
}
8697
return CACHE.compute(location, (repo, f) -> {
8798
if (f != null && (!f.isDone() || !f.isCompletedExceptionally() && !f.isCancelled())) {
8899
return f; // computation is running or has succeeded
89100
}
90-
CompletableFuture<Map<String, List<IVersionedId>>> future = new CompletableFuture<>();
101+
CompletableFuture<RepositoryMetadata> future = new CompletableFuture<>();
91102
// Fetching P2 repository information is a costly operation
92103
// time-wise. Thus it is done in a job.
93104
Job job = Job.create(NLS.bind(Messages.UpdateJob_P2DataFetch, repo), m -> {
94105
try {
95-
Map<String, List<IVersionedId>> units = toSortedMap(P2Fetcher.fetchAvailableUnits(repo, m));
96-
future.complete(units);
106+
RepositoryContent content = P2Fetcher.fetchAvailableUnits(repo, m);
107+
Map<String, List<IVersionedId>> units = toSortedMap(
108+
content.units().stream().map(iu -> new VersionedId(iu.getId(), iu.getVersion())));
109+
future.complete(new RepositoryMetadata(units, content.children()));
97110
} catch (Throwable e) {
98111
future.completeExceptionally(e);
99112
// Only log the failure, don't open an error-dialog.
@@ -115,11 +128,12 @@ private static Map<String, List<IVersionedId>> toSortedMap(Stream<IVersionedId>
115128
Collectors.groupingBy(IVersionedId::getId, LinkedHashMap::new, Collectors.toUnmodifiableList()));
116129
}
117130

118-
private static Map<String, List<IVersionedId>> getFutureValue(Future<Map<String, List<IVersionedId>>> future) {
131+
private static <T> T getFutureValue(Future<RepositoryMetadata> future, Function<RepositoryMetadata, T> getter,
132+
T defaultValue) {
119133
try {
120-
return future.get();
134+
return getter.apply(future.get());
121135
} catch (Exception e) { // interrupted, canceled or execution failure
122-
return Map.of();
136+
return defaultValue;
123137
}
124138
}
125139

ui/org.eclipse.pde.genericeditor.extension/src/org/eclipse/pde/internal/genericeditor/target/extension/p2/P2Fetcher.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,20 @@
1414
package org.eclipse.pde.internal.genericeditor.target.extension.p2;
1515

1616
import java.net.URI;
17+
import java.util.List;
1718
import java.util.stream.Stream;
1819

1920
import org.eclipse.core.runtime.CoreException;
2021
import org.eclipse.core.runtime.IProgressMonitor;
2122
import org.eclipse.core.runtime.SubMonitor;
2223
import org.eclipse.equinox.p2.core.IProvisioningAgent;
2324
import org.eclipse.equinox.p2.core.IProvisioningAgentProvider;
25+
import org.eclipse.equinox.p2.core.ProvisionException;
2426
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
25-
import org.eclipse.equinox.p2.metadata.IVersionedId;
26-
import org.eclipse.equinox.p2.metadata.VersionedId;
2727
import org.eclipse.equinox.p2.query.IQueryResult;
2828
import org.eclipse.equinox.p2.query.QueryUtil;
29+
import org.eclipse.equinox.p2.repository.ICompositeRepository;
30+
import org.eclipse.equinox.p2.repository.IRepository;
2931
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
3032
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
3133
import org.eclipse.pde.internal.genericeditor.target.extension.model.UnitNode;
@@ -39,14 +41,17 @@
3941
*/
4042
public class P2Fetcher {
4143

44+
public static record RepositoryContent(IQueryResult<IInstallableUnit> units, List<URI> children) {
45+
}
46+
4247
/**
4348
* This methods goes 'online' to make contact with a p2 repo and query it.
4449
*
4550
* @param repositoryLocation
46-
* URL string of a p2 repository
51+
* URI pointing to the location of a p2 repository
4752
* @return List of available installable unit models. See {@link UnitNode}
4853
*/
49-
public static Stream<IVersionedId> fetchAvailableUnits(URI repositoryLocation, IProgressMonitor monitor)
54+
public static RepositoryContent fetchAvailableUnits(URI repositoryLocation, IProgressMonitor monitor)
5055
throws CoreException {
5156
SubMonitor subMonitor = SubMonitor.convert(monitor, 31);
5257
BundleContext context = FrameworkUtil.getBundle(P2Fetcher.class).getBundleContext();
@@ -57,10 +62,23 @@ public static Stream<IVersionedId> fetchAvailableUnits(URI repositoryLocation, I
5762
IMetadataRepositoryManager manager = agent.getService(IMetadataRepositoryManager.class);
5863
IMetadataRepository repository = manager.loadRepository(repositoryLocation, subMonitor.split(30));
5964
IQueryResult<IInstallableUnit> allUnits = repository.query(QueryUtil.ALL_UNITS, subMonitor.split(1));
60-
return allUnits.stream().map(iu -> new VersionedId(iu.getId(), iu.getVersion()));
65+
List<URI> children = allChildren(repository, manager).toList();
66+
return new RepositoryContent(allUnits, children);
6167
} finally {
6268
context.ungetService(sr);
6369
}
6470
}
6571

72+
private static Stream<URI> allChildren(IRepository<?> repository, IMetadataRepositoryManager manager) {
73+
if (repository instanceof ICompositeRepository<?> composite) {
74+
return composite.getChildren().stream().flatMap(uri -> {
75+
try { // repository should already been cached
76+
return Stream.concat(Stream.of(uri), allChildren(manager.loadRepository(uri, null), manager));
77+
} catch (ProvisionException e) {
78+
return Stream.of(uri);
79+
}
80+
});
81+
}
82+
return Stream.empty();
83+
}
6684
}

0 commit comments

Comments
 (0)