1 /*******************************************************************************
2  * Copyright (c) 2008, 2010 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     EclipseSource - ongoing development
14  *******************************************************************************/
15 package org.eclipse.equinox.internal.p2.ui;
16 
17 import java.net.URI;
18 import java.util.*;
19 import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
20 import org.eclipse.equinox.internal.p2.ui.model.*;
21 import org.eclipse.equinox.internal.p2.ui.query.*;
22 import org.eclipse.equinox.p2.engine.IProfile;
23 import org.eclipse.equinox.p2.metadata.IInstallableUnit;
24 import org.eclipse.equinox.p2.metadata.IRequirement;
25 import org.eclipse.equinox.p2.metadata.expression.*;
26 import org.eclipse.equinox.p2.query.*;
27 import org.eclipse.equinox.p2.repository.artifact.ArtifactKeyQuery;
28 import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
29 import org.eclipse.equinox.p2.ui.Policy;
30 import org.eclipse.equinox.p2.ui.ProvisioningUI;
31 
32 /**
33  * Provides a default set of queries to drive the provisioning UI.
34  *
35  * @since 3.5
36  */
37 
38 public class QueryProvider {
39 
40 	private ProvisioningUI ui;
41 
42 	public static final int METADATA_REPOS = 1;
43 	public static final int ARTIFACT_REPOS = 2;
44 	public static final int PROFILES = 3;
45 	public static final int AVAILABLE_IUS = 4;
46 	public static final int AVAILABLE_UPDATES = 5;
47 	public static final int INSTALLED_IUS = 6;
48 	public static final int AVAILABLE_ARTIFACTS = 7;
49 
QueryProvider(ProvisioningUI ui)50 	public QueryProvider(ProvisioningUI ui) {
51 		this.ui = ui;
52 	}
53 
54 	/*
55 	 * Return a map of key/value pairs which are set to the environment settings
56 	 * for the given profile. May return <code>null</code> or an empty <code>Map</code>
57 	 * if the settings cannot be obtained.
58 	 */
getEnvFromProfile(IProfile profile)59 	private static Map<String, String> getEnvFromProfile(IProfile profile) {
60 		if (profile == null)
61 			return null;
62 		String environments = profile.getProperty(IProfile.PROP_ENVIRONMENTS);
63 		if (environments == null)
64 			return null;
65 		Map<String, String> result = new HashMap<>();
66 		for (StringTokenizer tokenizer = new StringTokenizer(environments, ","); tokenizer.hasMoreElements();) { //$NON-NLS-1$
67 			String entry = tokenizer.nextToken();
68 			int i = entry.indexOf('=');
69 			String key = entry.substring(0, i).trim();
70 			String value = entry.substring(i + 1).trim();
71 			result.put(key, value);
72 		}
73 		return result;
74 	}
75 
76 	// If we are supposed to filter out the results based on the environment settings in
77 	// the target profile then create a compound query otherwise just return the given query
createEnvironmentFilterQuery(IUViewQueryContext context, IProfile profile, IQuery<IInstallableUnit> query)78 	private IQuery<IInstallableUnit> createEnvironmentFilterQuery(IUViewQueryContext context, IProfile profile, IQuery<IInstallableUnit> query) {
79 		if (!context.getFilterOnEnv())
80 			return query;
81 		Map<String, String> environment = getEnvFromProfile(profile);
82 		if (environment == null)
83 			return query;
84 		IInstallableUnit envIU = InstallableUnit.contextIU(environment);
85 		IQuery<IInstallableUnit> filterQuery = QueryUtil.createMatchQuery("filter == null || $0 ~= filter", envIU); //$NON-NLS-1$
86 		return QueryUtil.createCompoundQuery(query, filterQuery, true);
87 	}
88 
getQueryDescriptor(final QueriedElement element)89 	public ElementQueryDescriptor getQueryDescriptor(final QueriedElement element) {
90 		// Initialize queryable, queryContext, and queryType from the element.
91 		// In some cases we override this.
92 		Policy policy = ui.getPolicy();
93 		IQueryable<?> queryable = element.getQueryable();
94 		int queryType = element.getQueryType();
95 		IUViewQueryContext context = element.getQueryContext();
96 		if (context == null) {
97 			context = ProvUI.getQueryContext(policy);
98 			context.setInstalledProfileId(ui.getProfileId());
99 		}
100 		switch (queryType) {
101 			case ARTIFACT_REPOS :
102 				queryable = new QueryableArtifactRepositoryManager(ui, false).locationsQueriable();
103 				return new ElementQueryDescriptor(queryable, new RepositoryLocationQuery(), new Collector<URI>(), new ArtifactRepositoryElementWrapper(null, element));
104 
105 			case AVAILABLE_IUS :
106 				// Things get more complicated if the user wants to filter out installed items.
107 				// This involves setting up a secondary query for installed content that the various
108 				// collectors will use to reject content.  We can't use a compound query because the
109 				// queryables are different (profile for installed content, repo for available content)
110 				AvailableIUWrapper availableIUWrapper;
111 				boolean showLatest = context.getShowLatestVersionsOnly();
112 				boolean hideInstalled = context.getHideAlreadyInstalled();
113 				IProfile targetProfile = null;
114 				String profileId = context.getInstalledProfileId();
115 				if (profileId != null) {
116 					targetProfile = ProvUI.getProfileRegistry(ui.getSession()).getProfile(profileId);
117 				}
118 
119 				IQuery<IInstallableUnit> topLevelQuery = policy.getVisibleAvailableIUQuery();
120 				IQuery<IInstallableUnit> categoryQuery = QueryUtil.createIUCategoryQuery();
121 
122 				topLevelQuery = createEnvironmentFilterQuery(context, targetProfile, topLevelQuery);
123 				categoryQuery = createEnvironmentFilterQuery(context, targetProfile, categoryQuery);
124 
125 				// Showing child IU's of a group of repositories, or of a single repository
126 				if (element instanceof MetadataRepositories || element instanceof MetadataRepositoryElement) {
127 					if (context.getViewType() == IUViewQueryContext.AVAILABLE_VIEW_FLAT || !context.getUseCategories()) {
128 						AvailableIUWrapper wrapper = new AvailableIUWrapper(queryable, element, false, context.getShowAvailableChildren());
129 						if (showLatest)
130 							topLevelQuery = QueryUtil.createLatestQuery(topLevelQuery);
131 						if (targetProfile != null)
132 							wrapper.markInstalledIUs(targetProfile, hideInstalled);
133 						return new ElementQueryDescriptor(queryable, topLevelQuery, new Collector<>(), wrapper);
134 					}
135 					// Installed content not a concern for collecting categories
136 					return new ElementQueryDescriptor(queryable, categoryQuery, new Collector<>(), new CategoryElementWrapper(queryable, element));
137 				}
138 
139 				// If it's a category or some other IUElement to drill down in, we get the requirements and show all requirements
140 				// that are also visible in the available list.
141 				if (element instanceof CategoryElement || (element instanceof IIUElement && ((IIUElement) element).shouldShowChildren())) {
142 					// children of a category should drill down according to the context.  If we aren't in a category, we are already drilling down and
143 					// continue to do so.
144 					boolean drillDownTheChildren = element instanceof CategoryElement ? context.getShowAvailableChildren() : true;
145 					IQuery<IInstallableUnit> memberOfCategoryQuery;
146 					if (element instanceof CategoryElement) {
147 						// We need an expression that uses the requirements of the element's requirements, which could be merged
148 						// from multiple category IUs shown as one in the UI.
149 						IExpression matchesRequirementsExpression = ExpressionUtil.parse("$0.exists(r | this ~= r)"); //$NON-NLS-1$
150 						memberOfCategoryQuery = QueryUtil.createMatchQuery(matchesRequirementsExpression, ((CategoryElement) element).getRequirements());
151 					} else {
152 						memberOfCategoryQuery = QueryUtil.createIUCategoryMemberQuery(((IIUElement) element).getIU());
153 					}
154 					memberOfCategoryQuery = createEnvironmentFilterQuery(context, targetProfile, memberOfCategoryQuery);
155 					availableIUWrapper = new AvailableIUWrapper(queryable, element, true, drillDownTheChildren);
156 					if (targetProfile != null)
157 						availableIUWrapper.markInstalledIUs(targetProfile, hideInstalled);
158 					// if it's a category, there is a special query.
159 					if (element instanceof CategoryElement) {
160 						if (showLatest)
161 							memberOfCategoryQuery = QueryUtil.createLatestQuery(memberOfCategoryQuery);
162 						return new ElementQueryDescriptor(queryable, memberOfCategoryQuery, new Collector<>(), availableIUWrapper);
163 					}
164 					// It is not a category, we want to traverse the requirements that are groups.
165 					IQuery<IInstallableUnit> query = QueryUtil.createCompoundQuery(topLevelQuery, new RequiredIUsQuery(((IIUElement) element).getIU()), true);
166 					if (showLatest)
167 						query = QueryUtil.createLatestQuery(query);
168 					// If it's not a category, these are generic requirements and should be filtered by the visibility property (topLevelQuery)
169 					return new ElementQueryDescriptor(queryable, query, new Collector<>(), availableIUWrapper);
170 				}
171 				return null;
172 
173 			case AVAILABLE_UPDATES :
174 				// This query can be used by the automatic updater in headless cases (checking for updates).
175 				// We traffic in IU's rather than wrapped elements
176 				IProfile profile;
177 				IInstallableUnit[] toUpdate = null;
178 				if (element instanceof Updates) {
179 					profile = ProvUI.getProfileRegistry(ui.getSession()).getProfile(((Updates) element).getProfileId());
180 					toUpdate = ((Updates) element).getIUs();
181 				} else {
182 					profile = ProvUI.getAdapter(element, IProfile.class);
183 				}
184 				if (profile == null)
185 					return null;
186 				if (toUpdate == null) {
187 					IQueryResult<IInstallableUnit> queryResult = profile.query(policy.getVisibleInstalledIUQuery(), null);
188 					toUpdate = queryResult.toArray(IInstallableUnit.class);
189 				}
190 				QueryableUpdates updateQueryable = new QueryableUpdates(ui, toUpdate);
191 				return new ElementQueryDescriptor(updateQueryable, context.getShowLatestVersionsOnly() ? QueryUtil.createLatestIUQuery() : QueryUtil.createIUAnyQuery(), new Collector<>());
192 
193 			case INSTALLED_IUS :
194 				// Querying of IU's.  We are drilling down into the requirements.
195 				if (element instanceof IIUElement && context.getShowInstallChildren()) {
196 					Collection<IRequirement> reqs = ((IIUElement) element).getRequirements();
197 					if (reqs.size() == 0)
198 						return null; // no children
199 					IExpression[] requirementExpressions = new IExpression[reqs.size()];
200 					int i = 0;
201 					for (IRequirement req : reqs) {
202 						requirementExpressions[i++] = req.getMatches();
203 					}
204 
205 					IExpressionFactory factory = ExpressionUtil.getFactory();
206 					IQuery<IInstallableUnit> meetsAnyRequirementQuery = QueryUtil.createMatchQuery(factory.or(requirementExpressions));
207 					IQuery<IInstallableUnit> visibleAsAvailableQuery = policy.getVisibleAvailableIUQuery();
208 					IQuery<IInstallableUnit> createCompoundQuery = QueryUtil.createCompoundQuery(visibleAsAvailableQuery, meetsAnyRequirementQuery, true);
209 					return new ElementQueryDescriptor(queryable, createCompoundQuery, new Collector<IInstallableUnit>(), new InstalledIUElementWrapper(queryable, element));
210 				}
211 				profile = ProvUI.getAdapter(element, IProfile.class);
212 				if (profile == null)
213 					return null;
214 				return new ElementQueryDescriptor(profile, policy.getVisibleInstalledIUQuery(), new Collector<IInstallableUnit>(), new InstalledIUElementWrapper(profile, element));
215 
216 			case METADATA_REPOS :
217 				if (element instanceof MetadataRepositories) {
218 					if (queryable == null) {
219 						queryable = new QueryableMetadataRepositoryManager(ui, ((MetadataRepositories) element).getIncludeDisabledRepositories()).locationsQueriable();
220 						element.setQueryable(queryable);
221 					}
222 					return new ElementQueryDescriptor(element.getQueryable(), new RepositoryLocationQuery(), new Collector<URI>(), new MetadataRepositoryElementWrapper(null, element));
223 				}
224 				return null;
225 
226 			case PROFILES :
227 				queryable = new QueryableProfileRegistry(ui);
228 				return new ElementQueryDescriptor(queryable, QueryUtil.createMatchQuery(IProfile.class, ExpressionUtil.TRUE_EXPRESSION), new Collector<>(), new ProfileElementWrapper(null, element));
229 
230 			case AVAILABLE_ARTIFACTS :
231 				if (!(queryable instanceof IArtifactRepository))
232 					return null;
233 				return new ElementQueryDescriptor(queryable, ArtifactKeyQuery.ALL_KEYS, new Collector<>(), new ArtifactKeyWrapper((IArtifactRepository) queryable, element));
234 
235 			default :
236 				return null;
237 		}
238 	}
239 }
240