/******************************************************************************* * Copyright (c) 2000, 2008 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.core; import java.util.*; import org.eclipse.core.runtime.CoreException; import org.eclipse.update.configuration.IConfiguredSite; import org.eclipse.update.core.*; import org.eclipse.update.core.model.ModelObject; /** * This class manages the reconciliation. */ public class SiteReconciler extends ModelObject { private SiteReconciler(LocalSite siteLocal) { //never instantiated } /** * Validate the list of configured features eliminating extra * entries (if possible). Make sure we do not leave configured * nested features with "holes" (ie. unconfigured children) */ public static void checkConfiguredFeatures(IConfiguredSite configuredSite) { // Note: if we hit errors in the various computation // methods and throw a CoreException, we will not catch it // in this method. Consequently we will not attempt to // unconfigure any "extra" features because we would // likely get it wrong. The platform will run with extra features // configured. The runtime will eliminate extra plugins based // on runtime binding rules. // determine "proposed" list of configured features ConfiguredSite cSite = (ConfiguredSite) configuredSite; // debug if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) { UpdateCore.debug("Validate configuration of site " + cSite.getSite().getURL()); //$NON-NLS-1$ } IFeatureReference[] configuredRefs = cSite.getConfiguredFeatures(); ArrayList allPossibleConfiguredFeatures = new ArrayList(); for (int i = 0; i < configuredRefs.length; i++) { try { IFeature feature = configuredRefs[i].getFeature(null); allPossibleConfiguredFeatures.add(feature); // debug if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) { UpdateCore.debug(" configured feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$ } } catch (CoreException e) { UpdateCore.warn("", e); //$NON-NLS-1$ } } // find top level features ArrayList topFeatures = computeTopFeatures(allPossibleConfiguredFeatures); // find non efix top level features ArrayList topNonEfixFeatures = getNonEfixFeatures(topFeatures); // expand non efix top level features (compute full nesting structures). ArrayList configuredFeatures = expandFeatures(topNonEfixFeatures, configuredSite); // retrieve efixes that patch enable feature // they must be kept enabled if (topFeatures.size() != topNonEfixFeatures.size()) { Map patches = getPatchesAsFeature(allPossibleConfiguredFeatures); if (!patches.isEmpty()) { // calculate efixes to enable List efixesToEnable = getPatchesToEnable(patches, configuredFeatures); // add efies to keep enable //add them to the enable list for (Iterator iter = efixesToEnable.iterator(); iter.hasNext();) { IFeature element = (IFeature) iter.next(); ArrayList expandedEfix = new ArrayList(); expandEfixFeature(element, expandedEfix, configuredSite); configuredFeatures.addAll(expandedEfix); } } } // compute extra features ArrayList extras = diff(allPossibleConfiguredFeatures, configuredFeatures); // unconfigure extra features ConfigurationPolicy cPolicy = cSite.getConfigurationPolicy(); for (int i = 0; i < extras.size(); i++) { IFeature feature = (IFeature) extras.get(i); IFeatureReference ref = cSite.getSite().getFeatureReference(feature); try { cPolicy.unconfigure(ref, true, false); // debug if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) { UpdateCore.debug("Unconfiguring \"extra\" feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$ } } catch (CoreException e) { UpdateCore.warn("", e); //$NON-NLS-1$ } } } /* * */ private static ArrayList computeTopFeatures(ArrayList features) { /* map of Feature by VersionedIdentifier */ Map topFeatures = new HashMap(features.size()); // start with the features passed in for (Iterator it = features.iterator(); it.hasNext();) { IFeature f = ((IFeature) it.next()); topFeatures.put(f.getVersionedIdentifier(), f); } // remove all features that nest in some other feature for (Iterator it = features.iterator(); it.hasNext();) { try { IIncludedFeatureReference[] children = ((IFeature) it.next()).getIncludedFeatureReferences(); for (int j = 0; j < children.length; j++) { try { topFeatures.remove(children[j].getVersionedIdentifier()); } catch (CoreException e1) { if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS) UpdateCore.warn("", e1); //$NON-NLS-1$ } } } catch (CoreException e) { UpdateCore.warn("", e); //$NON-NLS-1$ } } ArrayList list = new ArrayList(); list.addAll(topFeatures.values()); // debug if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) { UpdateCore.debug("Computed top-level features"); //$NON-NLS-1$ for (int i = 0; i < topFeatures.size(); i++) { UpdateCore.debug(" " + ((IFeature) list.get(i)).getVersionedIdentifier().toString()); //$NON-NLS-1$ } } return list; } /* * */ private static ArrayList expandFeatures(ArrayList features, IConfiguredSite configuredSite) { ArrayList result = new ArrayList(); // expand all top level features for (int i = 0; i < features.size(); i++) { expandFeature((IFeature) features.get(i), result, configuredSite); } return result; } /* * */ private static void expandFeature(IFeature feature, ArrayList features, IConfiguredSite configuredSite) { // add feature if (!features.contains(feature)) { features.add(feature); // debug if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) { UpdateCore.debug("Retaining configured feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$ } } // add nested children to the list IIncludedFeatureReference[] children = null; try { children = feature.getIncludedFeatureReferences(); } catch (CoreException e) { UpdateCore.warn("", e); //$NON-NLS-1$ return; } for (int j = 0; j < children.length; j++) { IFeature child = null; try { child = children[j].getFeature(null); } catch (CoreException e) { if (!UpdateManagerUtils.isOptional(children[j])) UpdateCore.warn("", e); //$NON-NLS-1$ // 25202 do not return right now, the peer children may be ok } if (child != null) expandFeature(child, features, configuredSite); } } /* * */ private static ArrayList diff(ArrayList left, ArrayList right) { ArrayList result = new ArrayList(); // determine difference (left "minus" right) for (int i = 0; i < left.size(); i++) { IFeature feature = (IFeature) left.get(i); if (!right.contains(feature)) result.add(feature); } return result; } /* * get the list of enabled patches */ private static Map getPatchesAsFeature(ArrayList allConfiguredFeatures) { // get all efixes and the associated patched features Map patches = new HashMap(); if (allConfiguredFeatures != null) { Iterator iter = allConfiguredFeatures.iterator(); while (iter.hasNext()) { List patchedFeaturesID = new ArrayList(); IFeature element = (IFeature) iter.next(); // add the patched feature identifiers for (int i = 0; i < element.getImports().length; i++) { if (element.getImports()[i].isPatch()) { VersionedIdentifier id = element.getImports()[i].getVersionedIdentifier(); if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) UpdateCore.debug("Found patch " + element + " for feature identifier " + id); //$NON-NLS-1$ //$NON-NLS-2$ patchedFeaturesID.add(id); } } if (!patchedFeaturesID.isEmpty()) { patches.put(element, patchedFeaturesID); } } } return patches; } /* * retruns the list of pathes-feature who patch enabled features */ private static List getPatchesToEnable(Map efixes, ArrayList configuredFeatures) { ArrayList enabledVersionedIdentifier = new ArrayList(); Iterator iter = configuredFeatures.iterator(); while (iter.hasNext()) { IFeature element = (IFeature) iter.next(); enabledVersionedIdentifier.add(element.getVersionedIdentifier()); } // loop through the patches List result = new ArrayList(); iter = efixes.keySet().iterator(); while (iter.hasNext()) { boolean toEnable = false; IFeature efixFeature = (IFeature) iter.next(); List patchedFeatures = (List) efixes.get(efixFeature); // loop through the 'patched features identifier' the for this patch // see if it the patch patches at least one enable feature Iterator patchedFeaturesIter = patchedFeatures.iterator(); while (patchedFeaturesIter.hasNext() && !toEnable) { VersionedIdentifier patchedFeatureID = (VersionedIdentifier) patchedFeaturesIter.next(); if (enabledVersionedIdentifier.contains(patchedFeatureID)) { toEnable = true; } } if (!toEnable) { if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) UpdateCore.debug("The Patch " + efixFeature + " does not patch any enabled features: it will be disabled"); //$NON-NLS-1$ //$NON-NLS-2$ } else { if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) UpdateCore.debug("The patch " + efixFeature + " will be enabled."); //$NON-NLS-1$ //$NON-NLS-2$ result.add(efixFeature); } } return result; } /* * returns the feature that are not patches */ private static ArrayList getNonEfixFeatures(ArrayList topFeatures) { Map efixFeatures = getPatchesAsFeature(topFeatures); Set keySet = efixFeatures.keySet(); if (keySet == null || keySet.isEmpty()) return topFeatures; Iterator iter = topFeatures.iterator(); ArrayList result = new ArrayList(); while (iter.hasNext()) { IFeature element = (IFeature) iter.next(); if (!keySet.contains(element)) { result.add(element); } } return result; } /* * only enable non-efix children recursively */ private static void expandEfixFeature(IFeature feature, ArrayList features, IConfiguredSite configuredSite) { // add feature if (!features.contains(feature)) { features.add(feature); // debug if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) { UpdateCore.debug("Retaining configured feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$ } } // add nested children to the list IIncludedFeatureReference[] children = null; try { children = feature.getIncludedFeatureReferences(); } catch (CoreException e) { UpdateCore.warn("", e); //$NON-NLS-1$ return; } for (int j = 0; j < children.length; j++) { IFeature child = null; try { child = children[j].getFeature(null); } catch (CoreException e) { if (!children[j].isOptional()) UpdateCore.warn("", e); //$NON-NLS-1$ // 25202 do not return right now, the peer children may be ok } if (child != null) { if (!UpdateCore.isPatch(child)) expandEfixFeature(child, features, configuredSite); } } } }