diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/query/QueryProviderTests.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/query/QueryProviderTests.java index e5c64f93a9..998232c40d 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/query/QueryProviderTests.java +++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/query/QueryProviderTests.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.HashMap; import org.eclipse.equinox.internal.p2.ui.model.*; +import org.eclipse.equinox.internal.p2.ui.query.IUViewQueryContext; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.operations.InstallOperation; @@ -87,4 +88,57 @@ public void testInstallDrilldown() { assertEquals("1.1", 1, children.length); } + /** + * Test that when grouping by category is disabled (flat view), + * items that are members of categories are still visible. + * This tests the fix for issue #990. + */ + public void testFlatViewShowsCategoryMembers() { + // Create a repository with: + // - A category that contains a non-group IU + // - A group IU (should always be visible) + HashMap categoryProperties = new HashMap<>(); + categoryProperties.put("org.eclipse.equinox.p2.type.category", "true"); + HashMap groupProperties = new HashMap<>(); + groupProperties.put("org.eclipse.equinox.p2.type.group", "true"); + + IInstallableUnit nonGroupIU = createIU("NonGroupIU", Version.create("1.0.0"), NO_REQUIRES, NO_PROPERTIES, true); + IInstallableUnit groupIU = createIU("GroupIU", Version.create("1.0.0"), NO_REQUIRES, groupProperties, true); + IInstallableUnit category = createIU("TestCategory", Version.create("1.0.0"), + createRequiredCapabilities(IInstallableUnit.NAMESPACE_IU_ID, "NonGroupIU"), categoryProperties, true); + + IMetadataRepository repo = createTestMetdataRepository(new IInstallableUnit[] { category, nonGroupIU, groupIU }); + + // Create a MetadataRepositories element with FLAT view context + IUViewQueryContext context = new IUViewQueryContext(IUViewQueryContext.AVAILABLE_VIEW_FLAT); + MetadataRepositories repoElement = new MetadataRepositories(context, ui, null); + repoElement.setQueryable(repo); + + MetadataRepositoryElement element = new MetadataRepositoryElement(repoElement, repo.getLocation(), true); + Object[] children = element.getChildren(element); + + // In flat view, we should see both the group IU and the non-group IU (because it's in a category) + // We should NOT see the category itself + boolean foundGroupIU = false; + boolean foundNonGroupIU = false; + boolean foundCategory = false; + + for (Object child : children) { + if (child instanceof AvailableIUElement) { + IInstallableUnit iu = ((AvailableIUElement) child).getIU(); + if (iu.getId().equals("GroupIU")) { + foundGroupIU = true; + } else if (iu.getId().equals("NonGroupIU")) { + foundNonGroupIU = true; + } + } else if (child instanceof CategoryElement) { + foundCategory = true; + } + } + + assertTrue("2.1 - Group IU should be visible in flat view", foundGroupIU); + assertTrue("2.2 - Non-group category member should be visible in flat view", foundNonGroupIU); + assertFalse("2.3 - Category should not be visible in flat view", foundCategory); + } + } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/QueryProvider.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/QueryProvider.java index a570f680c6..a29aaa3be3 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/QueryProvider.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/QueryProvider.java @@ -89,6 +89,53 @@ private IQuery createEnvironmentFilterQuery(IUViewQueryContext return QueryUtil.createCompoundQuery(query, filterQuery, true); } + /** + * Creates a query that matches IUs that are either groups or members of any category. + * This is used when showing a flat view to include both installable features (groups) + * and items that are part of categories. + * + * Note: This method creates a compound query from individual category member queries. + * While this could impact performance with a large number of categories, it follows + * the established pattern in this class and ensures consistency with category view behavior. + * + * @param queryable the queryable to search for categories + * @param topLevelQuery the base query for visible IUs (typically groups, with environment filter already applied) + * @param context the view query context + * @param targetProfile the target profile for environment filtering + * @return a compound query matching groups or category members + */ + private IQuery createGroupsOrCategoryMembersQuery(IQueryable queryable, IQuery topLevelQuery, IUViewQueryContext context, IProfile targetProfile) { + // First, get all categories + IQuery categoryQuery = QueryUtil.createIUCategoryQuery(); + categoryQuery = createEnvironmentFilterQuery(context, targetProfile, categoryQuery); + IQueryResult categories = queryable.query(categoryQuery, null); + + // If there are no categories, just return the top level query + if (categories.isEmpty()) { + return topLevelQuery; + } + + // Collect queries for all category members + List> memberQueries = new ArrayList<>(); + for (IInstallableUnit category : categories) { + IQuery memberQuery = QueryUtil.createIUCategoryMemberQuery(category); + // Apply environment filter to category members as well + memberQuery = createEnvironmentFilterQuery(context, targetProfile, memberQuery); + memberQueries.add(memberQuery); + } + + // If there are no valid category member queries, return just the top level query + if (memberQueries.isEmpty()) { + return topLevelQuery; + } + + // Combine all member queries with OR to get union of all category members + IQuery allCategoryMembersQuery = QueryUtil.createCompoundQuery(memberQueries, false); + + // Finally, combine groups OR category members + return QueryUtil.createCompoundQuery(topLevelQuery, allCategoryMembersQuery, false); + } + public ElementQueryDescriptor getQueryDescriptor(final QueriedElement element) { // Initialize queryable, queryContext, and queryType from the element. // In some cases we override this. @@ -128,6 +175,11 @@ public ElementQueryDescriptor getQueryDescriptor(final QueriedElement element) { // Showing child IU's of a group of repositories, or of a single repository if (element instanceof MetadataRepositories || element instanceof MetadataRepositoryElement) { if (context.getViewType() == IUViewQueryContext.AVAILABLE_VIEW_FLAT || !context.getUseCategories()) { + // When not grouping by categories, show both groups and items that are members of any category + // The cast is safe because in the AVAILABLE_IUS case, queryable is always an IQueryable + @SuppressWarnings("unchecked") + IQueryable iuQueryable = (IQueryable) queryable; + topLevelQuery = createGroupsOrCategoryMembersQuery(iuQueryable, topLevelQuery, context, targetProfile); AvailableIUWrapper wrapper = new AvailableIUWrapper(queryable, element, false, context.getShowAvailableChildren()); if (showLatest) { topLevelQuery = QueryUtil.createLatestQuery(topLevelQuery);