| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.update.search; |
| |
| import java.net.URL; |
| import java.util.ArrayList; |
| |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.update.core.*; |
| import org.eclipse.update.internal.core.*; |
| import org.eclipse.update.internal.search.UpdatePolicy; |
| |
| /** |
| * This class is central to update search. The search pattern |
| * is encapsulated in update search category, while the search |
| * scope is defined in the scope object. When these two objects |
| * are defined and set, search can be performed using the |
| * provided method. Search results are reported to the |
| * result collector, while search progress is tracked using |
| * the progress monitor. |
| * <p>Classes that implement <samp>IUpdateSearchResultCollector</samp> |
| * should call 'filter' to test if the match should be |
| * accepted according to the filters added to the request. |
| * |
| * <p> |
| * <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to |
| * change significantly before reaching stability. It is being made available at this early stage to solicit feedback |
| * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken |
| * (repeatedly) as the API evolves. |
| * </p> |
| * @see UpdateSearchScope |
| * @see IUpdateSearchCategory |
| * @since 3.0 |
| */ |
| public class UpdateSearchRequest { |
| private IUpdateSearchCategory category; |
| private UpdateSearchScope scope; |
| private boolean searchInProgress = false; |
| private AggregateFilter aggregateFilter = new AggregateFilter(); |
| |
| class AggregateFilter implements IUpdateSearchFilter { |
| private ArrayList filters; |
| public void addFilter(IUpdateSearchFilter filter) { |
| if (filters == null) |
| filters = new ArrayList(); |
| if (filters.contains(filter) == false) |
| filters.add(filter); |
| } |
| |
| public void removeFilter(IUpdateSearchFilter filter) { |
| if (filters == null) |
| return; |
| filters.remove(filter); |
| } |
| |
| public boolean accept(IFeature match) { |
| if (filters == null) |
| return true; |
| for (int i = 0; i < filters.size(); i++) { |
| IUpdateSearchFilter filter = (IUpdateSearchFilter) filters.get(i); |
| if (filter.accept(match) == false) |
| return false; |
| } |
| return true; |
| } |
| |
| public boolean accept(IFeatureReference match) { |
| if (filters == null) |
| return true; |
| for (int i = 0; i < filters.size(); i++) { |
| IUpdateSearchFilter filter = (IUpdateSearchFilter) filters.get(i); |
| if (filter.accept(match) == false) |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * The constructor that accepts the search category and |
| * scope objects. |
| * @param category the actual search pattern that should be applied |
| * @param scope a list of sites that need to be scanned during the search |
| */ |
| public UpdateSearchRequest( |
| IUpdateSearchCategory category, |
| UpdateSearchScope scope) { |
| this.category = category; |
| this.scope = scope; |
| } |
| /** |
| * Returns the search catagory used in this request. |
| * @return the search category |
| */ |
| |
| public IUpdateSearchCategory getCategory() { |
| return category; |
| } |
| |
| /** |
| * Returns the scope of this search request. |
| * @return search scope |
| */ |
| |
| public UpdateSearchScope getScope() { |
| return scope; |
| } |
| /** |
| * Adds a filter to this request. This method does nothing |
| * if search is alrady in progress. |
| * @param filter the filter |
| * @see UpdateSearchRequest#removeFilter |
| */ |
| public void addFilter(IUpdateSearchFilter filter) { |
| if (searchInProgress) |
| return; |
| aggregateFilter.addFilter(filter); |
| } |
| /** |
| * Removes the filter from this request. This method does |
| * nothing if search is alrady in progress. |
| * @param filter the filter to remove |
| * @see UpdateSearchRequest#addFilter |
| */ |
| |
| public void removeFilter(IUpdateSearchFilter filter) { |
| if (searchInProgress) |
| return; |
| aggregateFilter.removeFilter(filter); |
| } |
| |
| /** |
| * Sets the scope object. It is possible to reuse the search request |
| * object by modifying the scope and re-running the search. |
| * @param scope the new search scope |
| */ |
| public void setScope(UpdateSearchScope scope) { |
| this.scope = scope; |
| } |
| /** |
| * Tests whether this search request is current running. |
| * @return <samp>true</samp> if the search is currently running, <samp>false</samp> otherwise. |
| */ |
| public boolean isSearchInProgress() { |
| return searchInProgress; |
| } |
| |
| /** |
| * Runs the search using the category and scope configured into |
| * this request. As results arrive, they are passed to the |
| * search result collector object. |
| * @param collector matched features are passed to this object |
| * @param monitor used to track the search progress |
| * @throws CoreException |
| */ |
| public void performSearch( |
| IUpdateSearchResultCollector collector, |
| IProgressMonitor monitor) |
| throws CoreException { |
| ArrayList statusList = new ArrayList(); |
| |
| searchInProgress = true; |
| IUpdateSearchQuery[] queries = category.getQueries(); |
| IUpdateSearchSite[] candidates = scope.getSearchSites(); |
| URL updateMapURL = scope.getUpdateMapURL(); |
| boolean searchFeatureProvidedSites = scope.isFeatureProvidedSitesEnabled(); |
| |
| if (!monitor.isCanceled()) { |
| int nsearchsites = 0; |
| for (int i = 0; i < queries.length; i++) { |
| if (queries[i].getQuerySearchSite() != null) |
| nsearchsites++; |
| } |
| int ntasks = nsearchsites + queries.length * candidates.length; |
| if (updateMapURL!=null) ntasks++; |
| |
| monitor.beginTask("Searching...", ntasks); |
| |
| try { |
| UpdatePolicy updatePolicy=null; |
| if (updateMapURL!=null) { |
| updatePolicy = new UpdatePolicy(); |
| IStatus status =loadUpdatePolicy(updatePolicy, updateMapURL, new SubProgressMonitor(monitor, 1)); |
| if (status != null) |
| statusList.add(status); |
| } |
| for (int i = 0; i < queries.length; i++) { |
| IUpdateSearchQuery query = queries[i]; |
| IQueryUpdateSiteAdapter qsite = query.getQuerySearchSite(); |
| if (qsite != null && searchFeatureProvidedSites) { |
| // check for mapping |
| IUpdateSiteAdapter mappedSite = getMappedSite(updatePolicy, qsite); |
| // when there is no mapped site the feature is not updatable |
| if (mappedSite == null || mappedSite.getURL() == null) |
| continue; |
| SubProgressMonitor subMonitor = |
| new SubProgressMonitor(monitor, 1); |
| IStatus status = |
| searchOneSite( |
| mappedSite, |
| null, |
| query, |
| collector, |
| subMonitor); |
| if (status != null) |
| statusList.add(status); |
| if (monitor.isCanceled()) |
| break; |
| } |
| for (int j = 0; j < candidates.length; j++) { |
| if (monitor.isCanceled()) { |
| break; |
| } |
| IUpdateSearchSite source = candidates[j]; |
| SubProgressMonitor subMonitor = |
| new SubProgressMonitor(monitor, 1); |
| IStatus status = |
| searchOneSite( |
| source, |
| source.getCategoriesToSkip(), |
| query, |
| collector, |
| subMonitor); |
| if (status != null) |
| statusList.add(status); |
| } |
| if (monitor.isCanceled()) |
| break; |
| } |
| } catch (CoreException e) { |
| searchInProgress = false; |
| monitor.done(); |
| throw e; |
| } |
| } |
| searchInProgress = false; |
| monitor.done(); |
| |
| if (statusList.size() > 0) { |
| IStatus[] children = |
| (IStatus[]) statusList.toArray(new IStatus[statusList.size()]); |
| MultiStatus multiStatus = |
| new MultiStatus( |
| "org.eclipse.update.core", |
| ISite.SITE_ACCESS_EXCEPTION, |
| children, |
| Policy.bind("Search.networkProblems"), |
| null); |
| throw new CoreException(multiStatus); |
| } |
| } |
| |
| /* |
| * Load the update map using the map URL found in the scope. |
| */ |
| private IStatus loadUpdatePolicy(UpdatePolicy map, URL url, IProgressMonitor monitor) throws CoreException { |
| monitor.subTask("Loading update policy ..."); |
| try { |
| map.load(url, monitor); |
| monitor.worked(1); |
| } |
| catch (CoreException e) { |
| IStatus status = e.getStatus(); |
| if (status == null |
| || status.getCode() != ISite.SITE_ACCESS_EXCEPTION) |
| throw e; |
| monitor.worked(1); |
| return status; |
| } |
| return null; |
| } |
| /* |
| * See if this query site adapter is mapped in the map file |
| * to a different URL. |
| */ |
| private IUpdateSiteAdapter getMappedSite(UpdatePolicy policy, IQueryUpdateSiteAdapter qsite) { |
| if (policy!=null && policy.isLoaded()) { |
| IUpdateSiteAdapter mappedSite = policy.getMappedSite(qsite.getMappingId()); |
| if (mappedSite!=null) return mappedSite; |
| // no match - use original site if fallback allowed, or nothing. |
| return policy.isFallbackAllowed()? qsite : null; |
| } |
| return qsite; |
| } |
| |
| /* |
| * Search one site using the provided query. |
| */ |
| private IStatus searchOneSite( |
| IUpdateSiteAdapter siteAdapter, |
| String[] categoriesToSkip, |
| IUpdateSearchQuery query, |
| IUpdateSearchResultCollector collector, |
| SubProgressMonitor monitor) |
| throws CoreException { |
| String text = "Contacting " + siteAdapter.getLabel() + "..."; |
| monitor.subTask(text); |
| monitor.beginTask("", 10); |
| URL siteURL = siteAdapter.getURL(); |
| |
| ISite site; |
| try { |
| site = |
| SiteManager.getSite( |
| siteURL, |
| new SubProgressMonitor(monitor, 1)); |
| } catch (CoreException e) { |
| // Test the exception. If the exception is |
| // due to the site connection problems, |
| // allow the search to move on to |
| // the next site. Otherwise, |
| // rethrow the exception, causing the search |
| // to terminate. |
| IStatus status = e.getStatus(); |
| if (status == null) |
| // || status.getCode() != ISite.SITE_ACCESS_EXCEPTION) |
| throw e; |
| monitor.worked(10); |
| return status; |
| } |
| // If frozen connection was canceled, there will be no site. |
| if (site == null) { |
| monitor.worked(9); |
| return null; |
| } |
| |
| text = "Checking " + siteAdapter.getLabel() + "..."; |
| monitor.getWrappedProgressMonitor().subTask(text); |
| |
| query.run( |
| site, |
| categoriesToSkip, |
| aggregateFilter, |
| collector, |
| new SubProgressMonitor(monitor, 9)); |
| return null; |
| } |
| } |