blob: c21db09468ceb93e4d9b9bb64690c2ec5d6450d3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 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 {
IFeatureReference ref;
IInstallFeatureOperation patchFor;
boolean patch;
IInstallFeatureOperation job;
public Hit(IFeature candidate, IFeatureReference ref) {
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;
}
}