/**
   * Extract extension with the provided id from the provided extension.
   *
   * @param extension the extension
   * @param dependencyId the id of the dependency
   * @return the extension dependency or null if none has been found
   */
  private ExtensionDependency getDependency(Extension extension, String dependencyId) {
    for (ExtensionDependency dependency : extension.getDependencies()) {
      if (dependency.getId().equals(dependencyId)) {
        return dependency;
      }
    }

    return null;
  }
  /**
   * Check if provided id/version is compatible with provided extensions dependencies constraints.
   */
  private boolean isCompatible(
      Collection<? extends Extension> extensions, String id, Version version) {
    if (extensions != null) {
      for (Extension extension : extensions) {
        for (ExtensionDependency dependency : extension.getDependencies()) {
          if (dependency.getId().equals(id)) {
            if (!dependency.getVersionConstraint().isCompatible(version)) {
              return false;
            }
          }
        }
      }
    }

    return true;
  }
  /**
   * @param previousExtension the previous installed version of the extension to install
   * @param extension the new extension to install
   * @param dependency indicate if the extension is installed as a dependency
   * @param namespace the namespace where to install the extension
   * @return the install plan node for the provided extension
   * @param initialDependency the initial dependency used to resolve the extension
   * @throws InstallException error when trying to install provided extension
   */
  private ModifableExtensionPlanNode installExtension(
      InstalledExtension previousExtension,
      Extension extension,
      boolean dependency,
      String namespace,
      ExtensionDependency initialDependency)
      throws InstallException, ResolveException {
    // Is feature core extension
    for (String feature : extension.getFeatures()) {
      if (this.coreExtensionRepository.exists(feature)) {
        throw new InstallException(
            String.format("There is already a core extension with the id [%s]", feature));
      }
    }

    // Find all previous version of the extension
    Set<InstalledExtension> previousExtensions = new LinkedHashSet<InstalledExtension>();
    if (previousExtension != null) {
      previousExtensions.add(previousExtension);
    }
    if (!extension.getFeatures().isEmpty()) {
      for (String feature : extension.getFeatures()) {
        InstalledExtension installedExtension =
            checkAlreadyInstalledExtension(feature, extension.getId().getVersion(), namespace);
        if (installedExtension != null) {
          previousExtensions.add(installedExtension);
        }
      }
    }

    ExtensionHandler extensionHandler;

    // Is type supported ?
    try {
      extensionHandler =
          this.componentManager.getInstance(ExtensionHandler.class, extension.getType());
    } catch (ComponentLookupException e) {
      throw new InstallException(String.format("Unsupported type [%s]", extension.getType()), e);
    }

    // Is installing the extension allowed ?
    extensionHandler.checkInstall(extension, namespace, getRequest());

    // Check dependencies
    Collection<? extends ExtensionDependency> dependencies = extension.getDependencies();

    notifyPushLevelProgress(dependencies.size() + 1);

    try {
      List<ModifableExtensionPlanNode> children = null;
      if (!dependencies.isEmpty()) {
        children = new ArrayList<ModifableExtensionPlanNode>();
        for (ExtensionDependency dependencyDependency : extension.getDependencies()) {
          installExtensionDependency(dependencyDependency, namespace, children);

          notifyStepPropress();
        }
      }

      ModifableExtensionPlanNode node =
          initialDependency != null
              ? new ModifableExtensionPlanNode(
                  initialDependency, initialDependency.getVersionConstraint())
              : new ModifableExtensionPlanNode();

      node.setChildren(children);

      Action action;
      if (!previousExtensions.isEmpty()) {
        if (previousExtensions
                .iterator()
                .next()
                .getId()
                .getVersion()
                .compareTo(extension.getId().getVersion())
            > 0) {
          action = Action.DOWNGRADE;
        } else {
          action = Action.UPGRADE;
        }
      } else {
        action = Action.INSTALL;
      }

      node.setAction(
          new DefaultExtensionPlanAction(
              extension, previousExtensions, action, namespace, dependency));

      return node;
    } finally {
      notifyPopLevelProgress();
    }
  }