| /******************************************************************************* |
| * 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.internal.operations; |
| |
| import java.util.*; |
| |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.update.configuration.*; |
| import org.eclipse.update.core.*; |
| import org.eclipse.update.internal.core.*; |
| |
| /** |
| * This class is used to construct a joint feature hiearchy. |
| * Old feature reference represents feature that is |
| * found on in the current configuration. New feature |
| * reference is found in the feature that is an install/update |
| * candidate. The element is used to join nodes of the |
| * hiearchy formed by including features so that |
| * each node in the hiearchy contains references to the |
| * old and the new feature. Old and new features have |
| * the same IDs but different versions, except in |
| * the case of optional features, where the tree may |
| * be constructed to bring in an optional feature |
| * that was not installed initially. In that case, |
| * some nodes may have old an new references with the |
| * same ID and version. |
| * <p> |
| * Old feature reference may be null. That means |
| * that the older feature with the same ID but lower |
| * version was not found in the current configuration. |
| */ |
| public class FeatureHierarchyElement { |
| |
| private Object root; |
| private ArrayList children; |
| private IFeatureReference oldFeatureRef; |
| private IFeatureReference newFeatureRef; |
| private boolean checked; |
| private boolean optionalChildren; |
| private boolean nativeUpgrade = false; |
| |
| public FeatureHierarchyElement( |
| IFeatureReference oldRef, |
| IFeatureReference newRef) { |
| oldFeatureRef = oldRef; |
| newFeatureRef = newRef; |
| } |
| |
| public void setRoot(Object root) { |
| this.root = root; |
| } |
| |
| public Object getRoot() { |
| return root; |
| } |
| |
| /* |
| * Return true if element can be checked, false otherwise. |
| */ |
| public boolean isEditable() { |
| // cannot uncheck non-optional features |
| if (isOptional() == false) |
| return false; |
| // cannot uncheck optional feature that |
| // has already been installed |
| if (oldFeatureRef != null) |
| return false; |
| return true; |
| } |
| |
| /** |
| * A hirearchy node represents a 'false update' if |
| * both old and new references exist and both |
| * point to the feature with the same ID and version. |
| * These nodes will not any bytes to be downloaded - |
| * they simply exist to allow the hirarchy to |
| * reach the optional children that are missing |
| * and will be installed. |
| */ |
| |
| public boolean isFalseUpdate() { |
| if (oldFeatureRef != null && newFeatureRef != null) { |
| try { |
| return oldFeatureRef.getVersionedIdentifier().equals( |
| newFeatureRef.getVersionedIdentifier()); |
| } catch (CoreException e) { |
| } |
| } |
| return false; |
| } |
| /** |
| * Returns true if feature is included as optional. |
| */ |
| public boolean isOptional() { |
| return newFeatureRef instanceof IIncludedFeatureReference |
| && ((IIncludedFeatureReference) newFeatureRef).isOptional(); |
| } |
| /** |
| * Returns true if this optional feature is selected |
| * for installation. Non-optional features or non-editable |
| * features are always checked. |
| */ |
| public boolean isChecked() { |
| return checked; |
| } |
| |
| void setNativeUpgrade(boolean nativeUpgrade) { |
| this.nativeUpgrade = nativeUpgrade; |
| } |
| |
| /** |
| * Returns true if this optional feature should |
| * be enabled when installed. By default, all |
| * features in the hiearchy should be enabled. |
| * The exception is for optional features that |
| * are updated to a new version in case where |
| * the older version of the optional feature |
| * is disabled in the given configuration. |
| * In this case, the feature is |
| * updated and disabled in order to maintain |
| * its state. |
| */ |
| public boolean isEnabled(IInstallConfiguration config) { |
| if (nativeUpgrade) |
| return true; |
| if (isOptional() && oldFeatureRef != null) { |
| try { |
| IFeature oldFeature = oldFeatureRef.getFeature(null); |
| IConfiguredSite csite = |
| UpdateUtils.getConfigSite(oldFeature, config); |
| return csite.isConfigured(oldFeature); |
| } catch (CoreException e) { |
| } |
| } |
| return true; |
| } |
| |
| public IFeature getFeature() { |
| try { |
| IFeature feature = newFeatureRef.getFeature(null); |
| return feature; |
| } catch (CoreException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Selects an editable feature for installation. |
| */ |
| public void setChecked(boolean checked) { |
| this.checked = checked; |
| } |
| /** |
| * Returns label for UI presentation. |
| */ |
| public String getLabel() { |
| try { |
| return getFeatureLabel(newFeatureRef); |
| } catch (CoreException e) { |
| if (newFeatureRef instanceof IIncludedFeatureReference) { |
| String iname = |
| ((IIncludedFeatureReference) newFeatureRef).getName(); |
| if (iname != null) |
| return iname; |
| } |
| try { |
| VersionedIdentifier vid = |
| newFeatureRef.getVersionedIdentifier(); |
| return vid.toString(); |
| } catch (CoreException e2) { |
| } |
| } |
| return null; |
| } |
| /** |
| * Computes label from the feature. |
| */ |
| private String getFeatureLabel(IFeatureReference featureRef) |
| throws CoreException { |
| IFeature feature = featureRef.getFeature(null); |
| return feature.getLabel() |
| + " " //$NON-NLS-1$ |
| + feature.getVersionedIdentifier().getVersion().toString(); |
| } |
| /** |
| * Computes children by linking matching features from the |
| * old feature's and new feature's hierarchy. |
| */ |
| public FeatureHierarchyElement[] getChildren( |
| boolean update, |
| boolean patch, |
| IInstallConfiguration config) { |
| computeChildren(update, patch, config); |
| FeatureHierarchyElement[] array = |
| new FeatureHierarchyElement[children.size()]; |
| children.toArray(array); |
| return array; |
| } |
| |
| public FeatureHierarchyElement[] getChildren() { |
| if (children != null) { |
| FeatureHierarchyElement[] array = |
| new FeatureHierarchyElement[children.size()]; |
| children.toArray(array); |
| return array; |
| } |
| |
| return new FeatureHierarchyElement[0]; |
| } |
| /** |
| * Computes children of this node. |
| */ |
| public void computeChildren( |
| boolean update, |
| boolean patch, |
| IInstallConfiguration config) { |
| if (children == null) { |
| children = new ArrayList(); |
| try { |
| IFeature oldFeature = null; |
| IFeature newFeature = null; |
| newFeature = newFeatureRef.getFeature(null); |
| if (oldFeatureRef != null) |
| oldFeature = oldFeatureRef.getFeature(null); |
| optionalChildren = |
| computeElements( |
| oldFeature, |
| newFeature, |
| update, |
| patch, |
| config, |
| children); |
| for (int i = 0; i < children.size(); i++) { |
| FeatureHierarchyElement element = |
| (FeatureHierarchyElement) children.get(i); |
| element.setRoot(getRoot()); |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| /** |
| * |
| */ |
| public boolean hasOptionalChildren() { |
| return optionalChildren; |
| } |
| /** |
| * Adds checked optional features to the provided set. |
| */ |
| public void addCheckedOptionalFeatures( |
| boolean update, |
| boolean patch, |
| IInstallConfiguration config, |
| Set set) { |
| if (isOptional() && isChecked()) { |
| // Do not add checked optional features |
| // if this is an update case but |
| // the node is not a 'true' update |
| // (old and new feature are the equal) |
| if (!update || !isFalseUpdate()) |
| set.add(newFeatureRef); |
| } |
| FeatureHierarchyElement[] elements = getChildren(update, patch, config); |
| for (int i = 0; i < elements.length; i++) { |
| elements[i].addCheckedOptionalFeatures(update, patch, config, set); |
| } |
| } |
| |
| /** |
| * Computes first-level children of the linked hierarchy |
| * for the provided old and new features (same ID, different version |
| * where new version is greater or equal the old version). |
| * Old feature may be null. |
| */ |
| public static boolean computeElements( |
| IFeature oldFeature, |
| IFeature newFeature, |
| boolean update, |
| boolean patch, |
| IInstallConfiguration config, |
| ArrayList list) { |
| Object[] oldChildren = null; |
| Object[] newChildren = getIncludedFeatures(newFeature); |
| boolean optionalChildren = false; |
| |
| try { |
| if (oldFeature != null) { |
| oldChildren = getIncludedFeatures(oldFeature); |
| } |
| for (int i = 0; i < newChildren.length; i++) { |
| IFeatureReference oldRef = null; |
| IFeatureReference newRef = (IFeatureReference) newChildren[i]; |
| if (oldChildren != null) { |
| String newId = |
| newRef.getVersionedIdentifier().getIdentifier(); |
| |
| for (int j = 0; j < oldChildren.length; j++) { |
| IFeatureReference cref = |
| (IFeatureReference) oldChildren[j]; |
| try { |
| if (cref |
| .getVersionedIdentifier() |
| .getIdentifier() |
| .equals(newId)) { |
| oldRef = cref; |
| break; |
| } |
| } catch (CoreException ex) { |
| } |
| } |
| } else if (patch) { |
| // 30849 - find the old reference in the |
| // configuration. |
| if (!UpdateUtils.isPatch(newFeature)) { |
| oldRef = findPatchedReference(newRef, config); |
| } |
| } |
| // test if the old optional feature exists |
| if (oldRef != null |
| && ((oldRef instanceof IIncludedFeatureReference |
| && ((IIncludedFeatureReference) oldRef).isOptional()) |
| || patch)) { |
| try { |
| IFeature f = oldRef.getFeature(null); |
| if (f == null) |
| oldRef = null; |
| } catch (CoreException e) { |
| // missing |
| oldRef = null; |
| } |
| } |
| FeatureHierarchyElement element = |
| new FeatureHierarchyElement(oldRef, newRef); |
| // If this is an update (old feature exists), |
| // only check the new optional feature if the old exists. |
| // Otherwise, always check. |
| if (element.isOptional() && (update || patch)) { |
| element.setChecked(oldRef != null); |
| if (oldRef == null) { |
| // Does not have an old reference, |
| // but it may contain an older |
| // feature that may still qualify |
| // for update. For example, |
| // an older version may have been |
| // installed natively from the CD-ROM. |
| if (hasOlderVersion(newRef)) { |
| element.setNativeUpgrade(true); |
| element.setChecked(true); |
| } |
| } |
| } else |
| element.setChecked(true); |
| list.add(element); |
| element.computeChildren(update, patch, config); |
| if (element.isOptional() || element.hasOptionalChildren()) |
| optionalChildren = true; |
| } |
| } catch (CoreException e) { |
| } |
| return optionalChildren; |
| } |
| public static boolean hasOlderVersion(IFeatureReference newRef) { |
| try { |
| VersionedIdentifier vid = newRef.getVersionedIdentifier(); |
| PluginVersionIdentifier version = vid.getVersion(); |
| String mode = getUpdateVersionsMode(); |
| |
| IFeature[] allInstalled = |
| UpdateUtils.getInstalledFeatures(vid, false); |
| for (int i = 0; i < allInstalled.length; i++) { |
| IFeature candidate = allInstalled[i]; |
| PluginVersionIdentifier cversion = |
| candidate.getVersionedIdentifier().getVersion(); |
| // Verify that the difference qualifies as |
| // an update. |
| if (mode.equals(UpdateCore.EQUIVALENT_VALUE)) { |
| if (version.isEquivalentTo(cversion)) |
| return true; |
| } else if (mode.equals(UpdateCore.COMPATIBLE_VALUE)) { |
| if (version.isCompatibleWith(cversion)) |
| return true; |
| } |
| } |
| } catch (CoreException e) { |
| } |
| return false; |
| } |
| |
| private static IFeatureReference findPatchedReference( |
| IFeatureReference newRef, |
| IInstallConfiguration config) |
| throws CoreException { |
| VersionedIdentifier vid = newRef.getVersionedIdentifier(); |
| IConfiguredSite[] csites = config.getConfiguredSites(); |
| for (int i = 0; i < csites.length; i++) { |
| IConfiguredSite csite = csites[i]; |
| IFeatureReference[] refs = csite.getConfiguredFeatures(); |
| for (int j = 0; j < refs.length; j++) { |
| IFeatureReference ref = refs[j]; |
| VersionedIdentifier refVid = ref.getVersionedIdentifier(); |
| if (vid.getIdentifier().equals(refVid.getIdentifier())) |
| return ref; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns included feature references for the given reference. |
| */ |
| public static Object[] getIncludedFeatures(IFeatureReference ref) { |
| try { |
| IFeature feature = ref.getFeature(null); |
| return getIncludedFeatures(feature); |
| } catch (CoreException e) { |
| } |
| return new Object[0]; |
| } |
| |
| /** |
| * Returns included feature references for the given feature. |
| */ |
| |
| public static Object[] getIncludedFeatures(IFeature feature) { |
| try { |
| return feature.getIncludedFeatureReferences(); |
| } catch (CoreException e) { |
| } |
| return new Object[0]; |
| } |
| |
| private static String getUpdateVersionsMode() { |
| Preferences store = UpdateCore.getPlugin().getPluginPreferences(); |
| return store.getString(UpdateCore.P_UPDATE_VERSIONS); |
| } |
| } |