| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.update.internal.search; |
| |
| import java.util.ArrayList; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.PluginVersionIdentifier; |
| import org.eclipse.core.runtime.Preferences; |
| import org.eclipse.update.configuration.IConfiguredSite; |
| import org.eclipse.update.configuration.IInstallConfiguration; |
| import org.eclipse.update.configuration.ILocalSite; |
| import org.eclipse.update.core.IFeature; |
| import org.eclipse.update.core.IFeatureReference; |
| import org.eclipse.update.core.IIncludedFeatureReference; |
| import org.eclipse.update.core.ISite; |
| import org.eclipse.update.core.ISiteFeatureReference; |
| import org.eclipse.update.core.IURLEntry; |
| import org.eclipse.update.core.IUpdateConstants; |
| import org.eclipse.update.core.SiteManager; |
| import org.eclipse.update.core.VersionedIdentifier; |
| import org.eclipse.update.internal.core.Messages; |
| import org.eclipse.update.internal.core.UpdateCore; |
| import org.eclipse.update.internal.operations.FeatureHierarchyElement; |
| import org.eclipse.update.internal.operations.UpdateUtils; |
| import org.eclipse.update.operations.IInstallFeatureOperation; |
| import org.eclipse.update.operations.OperationsManager; |
| import org.eclipse.update.search.IQueryUpdateSiteAdapter; |
| import org.eclipse.update.search.IUpdateSearchFilter; |
| import org.eclipse.update.search.IUpdateSearchQuery; |
| import org.eclipse.update.search.IUpdateSearchResultCollector; |
| |
| public class UpdatesSearchCategory extends BaseSearchCategory { |
| private static final String CATEGORY_ID = |
| "org.eclipse.update.core.new-updates"; //$NON-NLS-1$ |
| private IFeature [] features; |
| private boolean automatic; |
| |
| class Candidate { |
| ArrayList children; |
| Candidate parent; |
| IFeatureReference ref; |
| public Candidate(IFeatureReference ref) { |
| this.ref = ref; |
| } |
| public Candidate(IFeatureReference ref, Candidate parent) { |
| this(ref); |
| this.parent = parent; |
| } |
| public void add(Candidate child) { |
| if (children == null) |
| children = new ArrayList(); |
| child.setParent(this); |
| children.add(child); |
| } |
| void setParent(Candidate parent) { |
| this.parent = parent; |
| } |
| public IFeatureReference getReference() { |
| return ref; |
| } |
| void setReference(IFeatureReference ref) { |
| this.ref = ref; |
| } |
| public IFeature getFeature(IProgressMonitor monitor) { |
| try { |
| return ref.getFeature(monitor); |
| } catch (CoreException e) { |
| return null; |
| } |
| } |
| public Candidate getParent() { |
| return parent; |
| } |
| public Candidate getRoot() { |
| Candidate root = this; |
| |
| while (root.getParent() != null) { |
| root = root.getParent(); |
| } |
| return root; |
| } |
| public IURLEntry getUpdateEntry() { |
| int location = IUpdateConstants.SEARCH_ROOT; |
| |
| if (ref instanceof IIncludedFeatureReference) |
| location = |
| ((IIncludedFeatureReference) ref).getSearchLocation(); |
| if (parent == null || location == IUpdateConstants.SEARCH_SELF) { |
| return getFeature(null).getUpdateSiteEntry(); |
| } |
| return getRoot().getUpdateEntry(); |
| } |
| public String toString() { |
| return ref.toString(); |
| } |
| public boolean equals(Object source) { |
| if (source instanceof Candidate) { |
| return this.ref.equals(((Candidate) source).getReference()); |
| } |
| if (source instanceof IFeatureReference) { |
| return this.ref.equals(source); |
| } |
| return false; |
| } |
| public void addToFlatList(ArrayList list, boolean updatableOnly) { |
| // add itself |
| if (!updatableOnly || isUpdatable()) |
| list.add(this); |
| // add children |
| if (children != null) { |
| for (int i = 0; i < children.size(); i++) { |
| Candidate child = (Candidate) children.get(i); |
| child.addToFlatList(list, updatableOnly); |
| } |
| } |
| } |
| public boolean isUpdatable() { |
| return (parent == null); |
| } |
| } |
| |
| private static class Hit { |
| IFeature candidate; |
| IFeatureReference ref; |
| IInstallFeatureOperation patchFor; |
| boolean patch; |
| IInstallFeatureOperation job; |
| |
| public Hit(IFeature candidate, IFeatureReference ref) { |
| this.candidate = candidate; |
| this.ref = ref; |
| } |
| public Hit(IFeature candidate, IFeatureReference ref, boolean patch) { |
| this(candidate, ref); |
| this.patch = patch; |
| } |
| |
| public Hit(IFeature candidate, IFeatureReference ref, IInstallFeatureOperation patchFor) { |
| this(candidate, ref, true); |
| this.patchFor = patchFor; |
| } |
| |
| public IInstallFeatureOperation getJob() { |
| if (job == null) { |
| try { |
| IFeature feature = ref.getFeature(null); |
| job = OperationsManager.getOperationFactory().createInstallOperation(null, feature, null, null, null); |
| } catch (CoreException e) { |
| UpdateCore.log(e); |
| } |
| } |
| return job; |
| } |
| |
| public boolean isPatch() { |
| return patch; |
| } |
| public IInstallFeatureOperation getPatchedJob() { |
| return patchFor; |
| } |
| } |
| |
| public class UpdateQuery implements IUpdateSearchQuery { |
| IFeature candidate; |
| IQueryUpdateSiteAdapter adapter; |
| |
| public UpdateQuery( |
| IFeature candidate, |
| IURLEntry updateEntry) { |
| this.candidate = candidate; |
| if (updateEntry != null && updateEntry.getURL() != null) |
| adapter = |
| new QueryUpdateSiteAdapter( |
| getLabelForEntry(updateEntry), |
| updateEntry.getURL(), |
| candidate.getVersionedIdentifier().getIdentifier()); |
| } |
| private String getLabelForEntry(IURLEntry entry) { |
| String label = entry.getAnnotation(); |
| if (label == null || label.length() == 0) |
| label = entry.getURL().toString(); |
| return label; |
| } |
| |
| public IQueryUpdateSiteAdapter getQuerySearchSite() { |
| return adapter; |
| } |
| private boolean isBroken() { |
| try { |
| IStatus status = |
| SiteManager.getLocalSite().getFeatureStatus(candidate); |
| return status.getSeverity() == IStatus.ERROR; |
| } catch (CoreException e) { |
| return false; |
| } |
| } |
| private boolean isMissingOptionalChildren(IFeature feature) { |
| try { |
| IIncludedFeatureReference[] children = |
| feature.getIncludedFeatureReferences(); |
| for (int i = 0; i < children.length; i++) { |
| IIncludedFeatureReference ref = children[i]; |
| try { |
| IFeature child = ref.getFeature(null); |
| // If we are here, the child is not missing. |
| // Check it's children recursively. |
| if (isMissingOptionalChildren(child)) |
| return true; |
| } catch (CoreException e) { |
| // Missing child. Return true if optional, |
| // otherwise it is a broken feature that we |
| // do not care about. |
| if (ref.isOptional()) { |
| return FeatureHierarchyElement.hasOlderVersion(ref); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| } |
| return false; |
| } |
| public void run( |
| ISite site, |
| String[] categoriesToSkip, |
| IUpdateSearchFilter filter, |
| IUpdateSearchResultCollector collector, |
| IProgressMonitor monitor) { |
| ArrayList hits = new ArrayList(); |
| boolean broken = isBroken(); |
| boolean missingOptionalChildren = false; |
| |
| // Don't bother to compute missing optional children |
| // if the feature is broken - all we want is to |
| // see if we should allow same-version re-install. |
| if (!broken) |
| missingOptionalChildren = isMissingOptionalChildren(candidate); |
| ISiteFeatureReference[] refs = site.getFeatureReferences(); |
| monitor.beginTask("", refs.length + 1); //$NON-NLS-1$ |
| ArrayList updateJobs = new ArrayList(); |
| for (int i = 0; i < refs.length; i++) { |
| ISiteFeatureReference ref = refs[i]; |
| try { |
| if (isNewerVersion(candidate.getVersionedIdentifier(),ref.getVersionedIdentifier())) { |
| Hit h = new Hit(candidate, ref); |
| hits.add(h); |
| IInstallFeatureOperation job = h.getJob(); |
| if (job != null) |
| updateJobs.add(job); |
| } else { |
| // accept the same feature if the installed |
| // feature is broken |
| if ((broken || missingOptionalChildren) |
| && candidate.getVersionedIdentifier().equals( |
| ref.getVersionedIdentifier())){ |
| hits.add(new Hit(candidate, ref)); |
| continue; |
| } |
| else { |
| // check for patches |
| if (isPatch(candidate, ref)){ |
| hits.add(new Hit(candidate, ref, true)); |
| continue; |
| } |
| } |
| } |
| } catch (CoreException e) { |
| } |
| monitor.worked(1); |
| if (monitor.isCanceled()){ |
| return; |
| } |
| |
| } |
| // accept patches for updated features |
| for (int n = 0; n < updateJobs.size(); n++) { |
| IInstallFeatureOperation job = (IInstallFeatureOperation) updateJobs |
| .get(n); |
| IFeature newCandidate = job.getFeature(); |
| for (int i = 0; i < refs.length; i++) { |
| ISiteFeatureReference ref = refs[i]; |
| if (isPatch(newCandidate, ref)) { |
| Hit h = new Hit(newCandidate, ref, job); |
| hits.add(h); |
| continue; |
| } |
| //monitor.worked(1); |
| if (monitor.isCanceled()) { |
| return; |
| } |
| } |
| } |
| if (hits.size() > 0) { |
| collectValidHits(hits, filter, collector); |
| } |
| monitor.worked(1); |
| monitor.done(); |
| } |
| |
| /** |
| * Returns IFeature associated with the IUpdateSearchQuery |
| */ |
| public IFeature getFeature() { |
| return candidate; |
| } |
| } |
| |
| private ArrayList candidates; |
| |
| public UpdatesSearchCategory() { |
| this(true); |
| } |
| |
| public UpdatesSearchCategory(boolean automatic) { |
| super(CATEGORY_ID); |
| this.automatic = automatic; |
| } |
| |
| private void collectValidHits( |
| ArrayList hits, |
| IUpdateSearchFilter filter, |
| IUpdateSearchResultCollector collector) { |
| //Object[] array = hits.toArray(); |
| IFeature topHit = null; |
| for (int i = 0; i < hits.size(); i++) { |
| Hit hit = (Hit) hits.get(i); |
| IInstallFeatureOperation job = hit.getJob(); |
| if (job == null) |
| continue; |
| // do not accept updates without a license |
| if (!UpdateUtils.hasLicense(job.getFeature())) { |
| UpdateCore.log(job.getFeature().getVersionedIdentifier() + ": " + Messages.DefaultFeatureParser_NoLicenseText, null); //$NON-NLS-1$ |
| continue; |
| } |
| IStatus status = null; |
| |
| // Fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=132450 |
| // Only validate for automatic updates because |
| // non-automatic once will arrive in the review wizard |
| // where additional validation will be performed |
| if (automatic) { |
| if( hit.getPatchedJob()==null){ |
| status = OperationsManager.getValidator().validatePendingInstall(job.getOldFeature(), job.getFeature()); |
| }else{ |
| status = OperationsManager.getValidator().validatePendingChanges(new IInstallFeatureOperation[]{hit.getPatchedJob(), job}); |
| } |
| } |
| if (status == null || status.getCode() == IStatus.WARNING) { |
| if (hit.isPatch()) { |
| IFeature patch = job.getFeature(); |
| // Do not add the patch if already installed |
| IFeature[] sameId = UpdateUtils.getInstalledFeatures(patch, false); |
| if (sameId.length==0) { |
| if (filter.accept(patch)) |
| collector.accept(patch); |
| } |
| } |
| else { |
| topHit = job.getFeature(); |
| if (filter.accept(topHit)) |
| collector.accept(topHit); |
| } |
| } |
| } |
| } |
| |
| private void initialize() { |
| candidates = new ArrayList(); |
| try { |
| ILocalSite localSite = SiteManager.getLocalSite(); |
| IInstallConfiguration config = localSite.getCurrentConfiguration(); |
| IConfiguredSite[] isites = config.getConfiguredSites(); |
| for (int i = 0; i < isites.length; i++) { |
| contributeCandidates(isites[i]); |
| } |
| } catch (CoreException e) { |
| UpdateCore.log( |
| Messages.UpdatesSearchCategory_errorSearchingForUpdates, |
| e); |
| } |
| } |
| |
| private void contributeCandidates(IConfiguredSite isite) |
| throws CoreException { |
| IFeatureReference[] refs = isite.getConfiguredFeatures(); |
| ArrayList candidatesPerSite = new ArrayList(); |
| for (int i = 0; i < refs.length; i++) { |
| IFeatureReference ref = refs[i]; |
| // Don't waste time searching for updates to |
| // patches. |
| try { |
| if (UpdateUtils.isPatch(ref.getFeature(null))) |
| continue; |
| } |
| catch (CoreException e) { |
| continue; |
| } |
| Candidate c = new Candidate(ref); |
| candidatesPerSite.add(c); |
| } |
| // Create a tree from a flat list |
| buildHierarchy(candidatesPerSite); |
| // Add the remaining root candidates to |
| // the global list of candidates. |
| candidates.addAll(candidatesPerSite); |
| } |
| |
| private void buildHierarchy(ArrayList candidates) throws CoreException { |
| Candidate[] array = |
| (Candidate[]) candidates.toArray(new Candidate[candidates.size()]); |
| // filter out included features so that only top-level features remain on the list |
| for (int i = 0; i < array.length; i++) { |
| Candidate parent = array[i]; |
| IFeature feature = parent.getFeature(null); |
| IFeatureReference[] included = |
| feature.getIncludedFeatureReferences(); |
| for (int j = 0; j < included.length; j++) { |
| IFeatureReference fref = included[j]; |
| Candidate child = findCandidate(candidates, fref); |
| if (child != null) { |
| parent.add(child); |
| child.setReference(fref); |
| candidates.remove(child); |
| } |
| } |
| } |
| } |
| private Candidate findCandidate(ArrayList list, IFeatureReference ref) { |
| for (int i = 0; i < list.size(); i++) { |
| Candidate c = (Candidate) list.get(i); |
| if (c.ref.equals(ref)) |
| return c; |
| } |
| return null; |
| } |
| |
| public IUpdateSearchQuery[] getQueries() { |
| initialize(); |
| ArrayList allCandidates = getAllCandidates(); |
| |
| IUpdateSearchQuery[] queries = |
| new IUpdateSearchQuery[allCandidates.size()]; |
| for (int i = 0; i < queries.length; i++) { |
| Candidate candidate = (Candidate) allCandidates.get(i); |
| IFeature feature = candidate.getFeature(null); |
| IURLEntry updateEntry = candidate.getUpdateEntry(); |
| if (feature == null) { |
| queries[i] = null; |
| } else { |
| queries[i] = new UpdateQuery(feature, updateEntry); |
| } |
| } |
| return queries; |
| } |
| |
| /** |
| * Sets the features for which new updates need to be found. If |
| * not set, updates will be searched for all the installed |
| * and configured features. |
| * @param features the features to search updates for |
| */ |
| public void setFeatures(IFeature [] features) { |
| this.features = features; |
| } |
| |
| /** |
| * Returns an array of features for which updates need to |
| * be found. |
| * @return an array of features or <samp>null</samp> if not |
| * set. |
| */ |
| public IFeature [] getFeatures() { |
| return features; |
| } |
| /** |
| * @param fvi |
| * @param cvi |
| * @return fvi < cvi |
| */ |
| private boolean isNewerVersion( |
| VersionedIdentifier fvi, |
| VersionedIdentifier cvi) { |
| if (!fvi.getIdentifier().equals(cvi.getIdentifier())) |
| return false; |
| PluginVersionIdentifier fv = fvi.getVersion(); |
| PluginVersionIdentifier cv = cvi.getVersion(); |
| String mode = getUpdateVersionsMode(); |
| boolean greater = cv.isGreaterThan(fv); |
| if (!greater) |
| return false; |
| if (mode.equals(UpdateCore.EQUIVALENT_VALUE)) |
| return cv.isEquivalentTo(fv); |
| else if (mode.equals(UpdateCore.COMPATIBLE_VALUE)) |
| return cv.isCompatibleWith(fv); |
| else |
| return false; |
| } |
| |
| private boolean isPatch(IFeature candidate, ISiteFeatureReference ref) { |
| if (ref.isPatch() == false) |
| return false; |
| try { |
| IFeature feature = ref.getFeature(null); |
| if( UpdateUtils.isPatch(candidate, feature)) |
| return true; |
| // Check if patch is for children |
| try { |
| IIncludedFeatureReference[] children = |
| candidate.getIncludedFeatureReferences(); |
| for (int i = 0; i < children.length; i++) { |
| IIncludedFeatureReference cref = children[i]; |
| try { |
| IFeature child = cref.getFeature(null); |
| if (isPatch(child, ref)) |
| return true; |
| } catch (CoreException e) { |
| } |
| } |
| } catch (CoreException e) { |
| } |
| return false; |
| } catch (CoreException e) { |
| return false; |
| } |
| } |
| |
| private String getUpdateVersionsMode() { |
| Preferences store = UpdateCore.getPlugin().getPluginPreferences(); |
| return store.getString(UpdateCore.P_UPDATE_VERSIONS); |
| } |
| /* |
| * This method recursively walks the list of candidates |
| * building the flat that starts with the roots but |
| * also includes all the children that are updatable |
| * (use 'include' clause with a match that is not 'perfect'). |
| */ |
| private ArrayList getAllCandidates() { |
| ArrayList selected = new ArrayList(); |
| for (int i=0; i<candidates.size(); i++) { |
| Candidate c = (Candidate)candidates.get(i); |
| if (isOnTheList(c)) |
| c.addToFlatList(selected, true); |
| } |
| return selected; |
| } |
| |
| private boolean isOnTheList(Candidate c) { |
| if (features==null) return true; |
| VersionedIdentifier vid; |
| try { |
| vid = c.getReference().getVersionedIdentifier(); |
| } |
| catch (CoreException e) { |
| return false; |
| } |
| for (int i=0; i<features.length; i++) { |
| IFeature feature = features[i]; |
| VersionedIdentifier fvid = feature.getVersionedIdentifier(); |
| if (fvid.equals(vid)) |
| return true; |
| } |
| return false; |
| } |
| } |