private ISiteFeature[] getFeaturesFromSelection(IStructuredSelection sel) {
   if (sel.isEmpty()) return new ISiteFeature[0];
   if (cachedSelection == sel) return cachedFeatures;
   cachedSelection = sel;
   ArrayList<ISiteFeature> features = new ArrayList<ISiteFeature>(sel.size());
   Iterator<?> iterator = sel.iterator();
   while (iterator.hasNext()) {
     Object next = iterator.next();
     if (next instanceof SiteFeatureAdapter) {
       if ((((SiteFeatureAdapter) next).feature) != null) {
         features.add(((SiteFeatureAdapter) next).feature);
       }
     }
   }
   cachedFeatures = features.toArray(new ISiteFeature[features.size()]);
   return cachedFeatures;
 }
  /** @return a map connecting IPath to the resolved bundles in that path */
  private Map getFileBundleMapping() {
    if (fFileBundleMapping != null) {
      return fFileBundleMapping;
    }

    // Map the bundles into their file locations
    fFileBundleMapping = new HashMap();
    for (Iterator iterator = fAllBundles.iterator(); iterator.hasNext(); ) {
      IResolvedBundle currentBundle = (IResolvedBundle) iterator.next();
      IPath parentPath = getParentPath(currentBundle);
      List bundles = (List) fFileBundleMapping.get(parentPath);
      if (bundles == null) {
        bundles = new ArrayList();
        bundles.add(currentBundle);
        fFileBundleMapping.put(parentPath, bundles);
      } else {
        bundles.add(currentBundle);
      }
    }
    return fFileBundleMapping;
  }
  private boolean handleRemove() {
    IStructuredSelection ssel = (IStructuredSelection) fCategoryViewer.getSelection();
    Iterator<?> iterator = ssel.iterator();
    boolean success = true;
    Set<?> removedCategories = new HashSet<Object>();
    while (iterator.hasNext()) {
      Object object = iterator.next();
      if (object == null) continue;
      if (object instanceof ISiteCategoryDefinition) {
        if (!handleRemoveCategoryDefinition((ISiteCategoryDefinition) object)) {
          success = false;
        }
      } else {
        // check if some of features was not removed during category removal
        SiteFeatureAdapter fa = (SiteFeatureAdapter) object;
        if (removedCategories.contains(fa.category)) continue;

        if (!handleRemoveSiteFeatureAdapter(fa)) {
          success = false;
        }
      }
    }
    return success;
  }
  /**
   * Uses the target state to determine all bundles required by the currently checked bundles and
   * returns them so they can be checked in the tree.
   *
   * @param allBundles list of all bundles to search requirements in
   * @param checkedBundles list of bundles to get requirements for
   * @return list of resolved bundles from the collection to be checked
   */
  private Object[] getRequiredPlugins(final Collection allBundles, final Object[] checkedBundles) {
    final Set dependencies = new HashSet();
    IRunnableWithProgress op =
        new IRunnableWithProgress() {
          public void run(IProgressMonitor monitor) {
            try {
              monitor.beginTask(Messages.TargetContentsGroup_5, 150);

              // Get all the bundle locations
              List allLocations = new ArrayList(allBundles.size());
              for (Iterator iterator = allBundles.iterator(); iterator.hasNext(); ) {
                IResolvedBundle current = (IResolvedBundle) iterator.next();
                try {
                  // Some bundles, such as those with errors, may not have locations
                  URI location = current.getBundleInfo().getLocation();
                  if (location != null) {
                    allLocations.add(new File(location).toURL());
                  }
                } catch (MalformedURLException e) {
                  PDEPlugin.log(e);
                  monitor.setCanceled(true);
                  return;
                }
              }
              if (monitor.isCanceled()) {
                return;
              }
              monitor.worked(20);

              // Create a PDE State containing all of the target bundles
              PDEState state =
                  new PDEState(
                      (URL[]) allLocations.toArray(new URL[allLocations.size()]),
                      true,
                      new SubProgressMonitor(monitor, 50));
              if (monitor.isCanceled()) {
                return;
              }

              // Figure out which of the models have been checked
              IPluginModelBase[] models = state.getTargetModels();
              List checkedModels = new ArrayList(checkedBundles.length);
              for (int i = 0; i < checkedBundles.length; i++) {
                if (checkedBundles[i] instanceof IResolvedBundle) {
                  BundleInfo bundle = ((IResolvedBundle) checkedBundles[i]).getBundleInfo();
                  for (int j = 0; j < models.length; j++) {
                    if (models[j]
                            .getBundleDescription()
                            .getSymbolicName()
                            .equals(bundle.getSymbolicName())
                        && models[j]
                            .getBundleDescription()
                            .getVersion()
                            .toString()
                            .equals(bundle.getVersion())) {
                      checkedModels.add(models[j]);
                      break;
                    }
                  }
                }
              }
              monitor.worked(20);
              if (monitor.isCanceled()) {
                return;
              }

              // Get implicit dependencies as a list of strings
              // This is wasteful since the dependency calculation puts them back into BundleInfos
              NameVersionDescriptor[] implicitDependencies =
                  fTargetDefinition.getImplicitDependencies();
              List implicitIDs = new ArrayList();
              if (implicitDependencies != null) {
                for (int i = 0; i < implicitDependencies.length; i++) {
                  implicitIDs.add(implicitDependencies[i].getId());
                }
              }
              monitor.worked(10);

              // Get all dependency bundles
              // exclude "org.eclipse.ui.workbench.compatibility" - it is only needed for pre-3.0
              // bundles
              dependencies.addAll(
                  DependencyManager.getDependencies(
                      checkedModels.toArray(),
                      (String[]) implicitIDs.toArray(new String[implicitIDs.size()]),
                      state.getState(),
                      new String[] {"org.eclipse.ui.workbench.compatibility"})); // $NON-NLS-1$
              monitor.worked(50);

            } finally {
              monitor.done();
            }
          }
        };
    try {
      // Calculate the dependencies
      new ProgressMonitorDialog(fTree.getControl().getShell()).run(true, true, op);

      // We want to check the dependents, the source of the dependents, and the source of the
      // originally checked
      Set checkedNames = new HashSet(checkedBundles.length);
      for (int i = 0; i < checkedBundles.length; i++) {
        if (checkedBundles[i] instanceof IResolvedBundle) {
          checkedNames.add(((IResolvedBundle) checkedBundles[i]).getBundleInfo().getSymbolicName());
        }
      }

      List toCheck = new ArrayList();
      for (Iterator iterator = fAllBundles.iterator(); iterator.hasNext(); ) {
        IResolvedBundle bundle = (IResolvedBundle) iterator.next();
        if (bundle.isSourceBundle()) {
          String name = bundle.getSourceTarget().getSymbolicName();
          if (name != null && (dependencies.contains(name) || checkedNames.contains(name))) {
            toCheck.add(bundle);
          }
        } else if (dependencies.contains(bundle.getBundleInfo().getSymbolicName())) {
          toCheck.add(bundle);
        }
      }
      return toCheck.toArray();
    } catch (InvocationTargetException e) {
      PDEPlugin.log(e);
    } catch (InterruptedException e) {
    }

    return new Object[0];
  }
  public void saveIncludedBundleState() {
    if (fFeaureModeButton.getSelection()) {
      // Create a list of checked bundle infos
      List included = new ArrayList();
      int missingCount = 0;
      Object[] checked = fTree.getCheckedLeafElements();
      for (int i = 0; i < checked.length; i++) {
        if (checked[i] instanceof IFeatureModel) {
          included.add(
              new NameVersionDescriptor(
                  ((IFeatureModel) checked[i]).getFeature().getId(),
                  null,
                  NameVersionDescriptor.TYPE_FEATURE));
        }
        if (checked[i] instanceof IResolvedBundle) {
          // Missing features are included as IResolvedBundles, save them as features instead
          if (((IResolvedBundle) checked[i]).getStatus().getCode()
              == IResolvedBundle.STATUS_PLUGIN_DOES_NOT_EXIST) {
            included.add(
                new NameVersionDescriptor(
                    ((IResolvedBundle) checked[i]).getBundleInfo().getSymbolicName(),
                    null,
                    NameVersionDescriptor.TYPE_PLUGIN));
            missingCount++;
          } else if (((IResolvedBundle) checked[i]).getStatus().getCode()
              == IResolvedBundle.STATUS_FEATURE_DOES_NOT_EXIST) {
            included.add(
                new NameVersionDescriptor(
                    ((IResolvedBundle) checked[i]).getBundleInfo().getSymbolicName(),
                    null,
                    NameVersionDescriptor.TYPE_FEATURE));
            missingCount++;
          } else {
            included.add(
                new NameVersionDescriptor(
                    ((IResolvedBundle) checked[i]).getBundleInfo().getSymbolicName(), null));
          }
        }
      }

      if (included.size() == 0) {
        fTargetDefinition.setIncluded(new NameVersionDescriptor[0]);
      } else if (included.size() == 0
          || included.size() - missingCount
              == fTargetDefinition.getAllFeatures().length
                  + ((TargetDefinition) fTargetDefinition).getOtherBundles().length) {
        fTargetDefinition.setIncluded(null);
      } else {
        fTargetDefinition.setIncluded(
            (NameVersionDescriptor[]) included.toArray(new NameVersionDescriptor[included.size()]));
      }
    } else {
      // Figure out if there are multiple bundles sharing the same id
      Set multi = new HashSet(); // BSNs of bundles with multiple versions available
      Set all = new HashSet();
      for (Iterator iterator = fAllBundles.iterator(); iterator.hasNext(); ) {
        IResolvedBundle rb = (IResolvedBundle) iterator.next();
        if (!all.add(rb.getBundleInfo().getSymbolicName())) {
          multi.add(rb.getBundleInfo().getSymbolicName());
        }
      }

      // Create a list of checked bundle infos
      List included = new ArrayList();
      Object[] checked = fTree.getCheckedLeafElements();
      for (int i = 0; i < checked.length; i++) {
        if (checked[i] instanceof IResolvedBundle) {
          // Create the bundle info object
          String bsn = ((IResolvedBundle) checked[i]).getBundleInfo().getSymbolicName();
          NameVersionDescriptor info = null;
          if (multi.contains(bsn)) {
            // include version info
            info =
                new NameVersionDescriptor(
                    bsn, ((IResolvedBundle) checked[i]).getBundleInfo().getVersion());
          } else {
            // don't store version info
            info = new NameVersionDescriptor(bsn, null);
          }
          included.add(info);
        }
      }

      if (included.size() == 0) {
        fTargetDefinition.setIncluded(new NameVersionDescriptor[0]);
      } else if (included.size() == fAllBundles.size() + fMissing.size()) {
        fTargetDefinition.setIncluded(null);
      } else {
        fTargetDefinition.setIncluded(
            (NameVersionDescriptor[]) included.toArray(new NameVersionDescriptor[included.size()]));
      }
    }
  }