Skip to content

Commit 957cb89

Browse files
chrisruegerlaeubi
authored andcommitted
RepoView: add missing bnd tools features
- add features added in bndtools RepositoriesView since pde migration Repo View: Show workspace-repo if bnd workspace - if there is a cnf project (which is the marker for a bnd workspace) then we now show the WorkspaceRepository which contains all projects better detect BND workspace add icon for workspacerepo fix compiler warnings
1 parent 667475f commit 957cb89

File tree

5 files changed

+181
-8
lines changed

5 files changed

+181
-8
lines changed
390 Bytes
Loading

ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/RepositoryUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.osgi.util.tracker.ServiceTracker;
3636

3737
import aQute.bnd.build.Workspace;
38+
import aQute.bnd.build.WorkspaceLayout;
3839
import aQute.bnd.memoize.Memoize;
3940
import aQute.bnd.service.RegistryPlugin;
4041
import aQute.bnd.service.RepositoryPlugin;
@@ -53,6 +54,7 @@ public class RepositoryUtils {
5354
return tracker.orElse(null);
5455
}, Objects::nonNull);
5556
}
57+
5658

5759
public static List<RepositoryPlugin> listRepositories(final Workspace localWorkspace, final boolean hideCache) {
5860
if (localWorkspace == null) {
@@ -70,6 +72,14 @@ public static List<RepositoryPlugin> listRepositories(final Workspace localWorks
7072
// Workspace bndWorkspace = Central.getWorkspaceIfPresent();
7173
// if ((bndWorkspace == localWorkspace) && !bndWorkspace.isDefaultWorkspace())
7274
// repos.add(Central.getWorkspaceRepository());
75+
76+
// TODO this is not perfect, because it is only working
77+
// if you are selecting a bnd project. Would be better if bnd WorkspaceRepository is added always
78+
// e.g. if there is at least one bnd project
79+
if (WorkspaceLayout.BND == localWorkspace.getLayout() && !localWorkspace.isDefaultWorkspace()) {
80+
repos.add(localWorkspace.getWorkspaceRepository());
81+
}
82+
7383

7484
// Add the repos from the provided workspace
7585
for (RepositoryPlugin plugin : plugins) {

ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/repo/RepositoryTreeContentProvider.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Collections;
2828
import java.util.EnumSet;
2929
import java.util.HashMap;
30+
import java.util.LinkedHashMap;
3031
import java.util.LinkedHashSet;
3132
import java.util.List;
3233
import java.util.Map;
@@ -68,6 +69,11 @@ public class RepositoryTreeContentProvider implements ITreeContentProvider {
6869

6970
private String rawFilter = null;
7071
private String wildcardFilter = null;
72+
/**
73+
* Number of filter results to keep per repo. This is to avoid memory leaks
74+
* if you search with lots of different filter strings.
75+
*/
76+
private static final int MAX_CACHED_FILTER_RESULTS = 10;
7177
private boolean showRepos = true;
7278

7379
private Requirement requirementFilter = null;
@@ -306,7 +312,8 @@ Object[] getRepositoryBundles(final RepositoryPlugin repoPlugin) {
306312
* this node and the next time this method gets called the 'results'
307313
* will be available in the cache
308314
*/
309-
Map<String, Object[]> listResults = repoPluginListResults.computeIfAbsent(repoPlugin, p -> new HashMap<>());
315+
Map<String, Object[]> listResults = repoPluginListResults.computeIfAbsent(repoPlugin,
316+
p -> createLRUMap(MAX_CACHED_FILTER_RESULTS));
310317

311318
result = listResults.get(wildcardFilter);
312319

@@ -335,7 +342,7 @@ protected IStatus run(IProgressMonitor monitor) {
335342
}
336343

337344
Map<String, Object[]> listResults = repoPluginListResults.computeIfAbsent(repoPlugin,
338-
p -> new HashMap<>());
345+
p -> createLRUMap(MAX_CACHED_FILTER_RESULTS));
339346
listResults.put(wildcardFilter, jobresult);
340347

341348
Display.getDefault()
@@ -362,7 +369,7 @@ protected IStatus run(IProgressMonitor monitor) {
362369

363370
if (status != null && status.isOK()) {
364371
Map<String, Object[]> fastResults = repoPluginListResults.computeIfAbsent(repoPlugin,
365-
p -> new HashMap<>());
372+
p -> createLRUMap(MAX_CACHED_FILTER_RESULTS));
366373
result = fastResults.get(wildcardFilter);
367374
} else {
368375
Object[] loading = new Object[] {
@@ -392,4 +399,19 @@ private Object[] searchR5Repository(RepositoryPlugin repoPlugin, Repository osgi
392399
result = resultSet.toArray();
393400
return result;
394401
}
402+
403+
404+
// Define a LRU-like inner map (max n entries)
405+
private static Map<String, Object[]> createLRUMap(int n) {
406+
return new LinkedHashMap<String, Object[]>(n + 1, 1.0f, true) {
407+
private static final long serialVersionUID = 1L;
408+
409+
@Override
410+
protected boolean removeEldestEntry(Map.Entry<String, Object[]> eldest) {
411+
// Auto-remove oldest when size > n
412+
// but always keep the 'null' key which is '*'
413+
return size() > n && eldest.getKey() != null;
414+
}
415+
};
416+
}
395417
}

ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/model/repo/SearchableRepositoryTreeContentProvider.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
*******************************************************************************/
1515
package org.eclipse.pde.bnd.ui.model.repo;
1616

17+
import java.util.ArrayList;
18+
import java.util.Arrays;
19+
import java.util.LinkedList;
20+
import java.util.List;
21+
import java.util.Queue;
22+
1723
import aQute.bnd.service.RepositoryPlugin;
1824
import aQute.bnd.service.repository.SearchableRepository;
1925

@@ -42,4 +48,32 @@ Object[] getRepositoryBundles(RepositoryPlugin repo) {
4248

4349
return result;
4450
}
51+
52+
public List<RepositoryBundleVersion> allRepoBundleVersions(final RepositoryPlugin rp) {
53+
Object[] result = getChildren(rp);
54+
55+
List<RepositoryBundleVersion> allChildren = new ArrayList<>();
56+
Queue<Object> queue = new LinkedList<>();
57+
58+
if (result != null) {
59+
queue.addAll(Arrays.asList(result));
60+
}
61+
62+
while (!queue.isEmpty()) {
63+
Object currentChild = queue.poll();
64+
65+
if (currentChild instanceof RepositoryBundleVersion rpv) {
66+
allChildren.add(rpv);
67+
}
68+
else if (currentChild instanceof RepositoryResourceElement rre) {
69+
allChildren.add(rre.getRepositoryBundleVersion());
70+
}
71+
72+
Object[] childrenOfChild = getChildren(currentChild);
73+
if (childrenOfChild != null) {
74+
queue.addAll(Arrays.asList(childrenOfChild));
75+
}
76+
}
77+
return allChildren;
78+
}
4579
}

ui/org.eclipse.pde.bnd.ui/src/org/eclipse/pde/bnd/ui/views/repository/RepositoriesView.java

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.nio.file.Files;
4040
import java.util.ArrayList;
4141
import java.util.Arrays;
42+
import java.util.Collection;
4243
import java.util.Collections;
4344
import java.util.IdentityHashMap;
4445
import java.util.Iterator;
@@ -84,6 +85,7 @@
8485
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
8586
import org.eclipse.jface.viewers.ISelection;
8687
import org.eclipse.jface.viewers.IStructuredSelection;
88+
import org.eclipse.jface.viewers.TreePath;
8789
import org.eclipse.jface.viewers.TreeViewer;
8890
import org.eclipse.jface.viewers.Viewer;
8991
import org.eclipse.jface.viewers.ViewerDropAdapter;
@@ -151,6 +153,8 @@
151153
import aQute.bnd.build.Workspace;
152154
import aQute.bnd.exceptions.Exceptions;
153155
import aQute.bnd.http.HttpClient;
156+
import aQute.bnd.osgi.resource.FilterParser.PackageExpression;
157+
import aQute.bnd.osgi.resource.ResourceUtils;
154158
import aQute.bnd.service.Actionable;
155159
import aQute.bnd.service.Refreshable;
156160
import aQute.bnd.service.Registry;
@@ -198,6 +202,9 @@ public void workspaceOfflineChanged(boolean offline) {
198202
private final IObservableValue<String> workspaceName = new WritableValue<>();
199203
private final IObservableValue<String> workspaceDescription = new WritableValue<>();
200204

205+
private Object[] lastExpandedElements;
206+
private TreePath[] lastExpandedPaths;
207+
201208
@Override
202209
public void createPartControl(final Composite parent) {
203210
// CREATE CONTROLS
@@ -601,11 +608,28 @@ boolean addFilesToRepository(RepositoryPlugin repo, File[] files) {
601608
}
602609

603610
private void updatedFilter(String filterString) {
604-
contentProvider.setFilter(filterString);
605-
viewer.refresh();
606-
if (filterString != null) {
607-
viewer.expandToLevel(2);
608-
}
611+
viewer.getTree()
612+
.setRedraw(false);
613+
614+
try {
615+
if (filterString == null || filterString.isEmpty()) {
616+
// Restore previous state when clearing filter
617+
contentProvider.setFilter(null);
618+
viewer.refresh(); // Required to clear filter
619+
restoreExpansionState();
620+
viewer.refresh();
621+
} else {
622+
// Save state before applying new filter
623+
saveExpansionState();
624+
contentProvider.setFilter(filterString);
625+
viewer.refresh();
626+
viewer.expandToLevel(2);
627+
}
628+
629+
} finally {
630+
viewer.getTree()
631+
.setRedraw(true);
632+
}
609633
}
610634

611635
void createActions() {
@@ -1209,10 +1233,78 @@ private void addCopyToClipboardSubMenueEntries(Actionable act, final RepositoryP
12091233

12101234
if ((act instanceof Repository) || (act instanceof RepositoryPlugin)) {
12111235
hmenu.add(createContextMenueCopyInfoRepo(act, rp, clipboard));
1236+
hmenu.add(createContextMenueCopyBundlesWithSelfImports(act, rp, clipboard));
12121237
}
12131238

12141239
}
12151240

1241+
1242+
private HierarchicalLabel<Action> createContextMenueCopyBundlesWithSelfImports(Actionable act, final RepositoryPlugin rp,
1243+
final Clipboard clipboard) {
1244+
return new HierarchicalLabel<Action>("Copy to clipboard :: Bundles with substitution packages (self-imports)",
1245+
(label) -> createAction(label.getLeaf(),
1246+
"Add list of bundles containing packages which are imported and exported in their Manifest.", true,
1247+
false, rp, () -> {
1248+
1249+
final StringBuilder sb = new StringBuilder(
1250+
"Shows list of bundles in the repository '" + rp.getName()
1251+
+ "' containing substitution packages / self-imports (i.e. same package imported and exported) in their Manifest. \n"
1252+
+ "Note: a missing version range can cause wiring / resolution problems.\n"
1253+
+ "See https://docs.osgi.org/specification/osgi.core/8.0.0/framework.module.html#i3238802 "
1254+
+ "and https://docs.osgi.org/specification/osgi.core/8.0.0/framework.module.html#framework.module-import.export.same.package "
1255+
+ "for more information."
1256+
+ "\n\n");
1257+
1258+
for (RepositoryBundleVersion rpv : contentProvider.allRepoBundleVersions(rp)) {
1259+
org.osgi.resource.Resource r = rpv.getResource();
1260+
Collection<PackageExpression> selfImports = ResourceUtils
1261+
.getSubstitutionPackages(r);
1262+
1263+
if (!selfImports.isEmpty()) {
1264+
long numWithoutRange = selfImports.stream()
1265+
.filter(pckExp -> pckExp.getRangeExpression() == null)
1266+
.count();
1267+
1268+
// Main package information
1269+
sb.append(r.toString())
1270+
.append("\n");
1271+
sb.append(" Substitution packages: ")
1272+
.append(selfImports.size());
1273+
1274+
// Additional information about packages without
1275+
// version range
1276+
if (numWithoutRange > 0) {
1277+
sb.append(" (")
1278+
.append(numWithoutRange)
1279+
.append(" without version range)");
1280+
}
1281+
sb.append("\n");
1282+
1283+
// List of substitution packages
1284+
sb.append(" [\n");
1285+
for (PackageExpression pckExp : selfImports) {
1286+
sb.append(" ")
1287+
.append(pckExp.toString())
1288+
.append(",\n");
1289+
}
1290+
// Remove the last comma and newline
1291+
if (!selfImports.isEmpty()) {
1292+
sb.setLength(sb.length() - 2);
1293+
}
1294+
sb.append("\n ]\n\n");
1295+
}
1296+
1297+
}
1298+
1299+
if (sb.isEmpty()) {
1300+
clipboard.copy("-Empty-");
1301+
} else {
1302+
clipboard.copy(sb.toString());
1303+
}
1304+
1305+
}));
1306+
}
1307+
12161308
private HierarchicalLabel<Action> createContextMenueCopyInfoRepo(Actionable act, final RepositoryPlugin rp,
12171309
final Clipboard clipboard) {
12181310
return new HierarchicalLabel<Action>("Copy to clipboard :: Copy info", (label) -> createAction(label.getLeaf(),
@@ -1330,6 +1422,21 @@ private void handleOpenAdvancedSearch(Event event) {
13301422
}
13311423
}
13321424

1425+
private void saveExpansionState() {
1426+
lastExpandedElements = viewer.getExpandedElements();
1427+
lastExpandedPaths = viewer.getExpandedTreePaths();
1428+
}
1429+
1430+
private void restoreExpansionState() {
1431+
if (lastExpandedElements != null) {
1432+
viewer.setExpandedElements(lastExpandedElements);
1433+
}
1434+
if (lastExpandedPaths != null) {
1435+
viewer.setExpandedTreePaths(lastExpandedPaths);
1436+
}
1437+
}
1438+
1439+
13331440
@Override
13341441
public Workspace getWorkspace() {
13351442
return this.workspace;

0 commit comments

Comments
 (0)