/**
   * {@inheritDoc}
   *
   * @see
   *     org.xwiki.extension.handler.ExtensionHandlerManager#uninstall(org.xwiki.extension.LocalExtension,
   *     java.lang.String)
   */
  public void uninstall(LocalExtension localExtension, String namespace) throws UninstallException {
    try {
      // Load extension
      ExtensionHandler extensionHandler = getExtensionHandler(localExtension);

      extensionHandler.uninstall(localExtension, namespace);
    } catch (ComponentLookupException e) {
      throw new UninstallException(LOOKUPERROR + '[' + localExtension + ']');
    }
  }
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.xwiki.extension.handler.ExtensionHandlerManager#upgrade(org.xwiki.extension.LocalExtension,
   *     org.xwiki.extension.LocalExtension, java.lang.String)
   */
  public void upgrade(
      LocalExtension previousLocalExtension, LocalExtension newLocalExtension, String namespace)
      throws InstallException {
    try {
      // Load extension
      ExtensionHandler extensionInstaller = getExtensionHandler(previousLocalExtension);

      extensionInstaller.upgrade(previousLocalExtension, newLocalExtension, namespace);
    } catch (ComponentLookupException e) {
      throw new InstallException(LOOKUPERROR + '[' + newLocalExtension + ']');
    }
  }
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.xwiki.extension.handler.ExtensionHandlerManager#install(org.xwiki.extension.LocalExtension,
   *     java.lang.String)
   */
  public void install(LocalExtension localExtension, String namespace) throws InstallException {
    ExtensionHandler extensionHandler;
    try {
      // Load extension
      extensionHandler = getExtensionHandler(localExtension);
    } catch (ComponentLookupException e) {
      throw new InstallException(LOOKUPERROR + '[' + localExtension + ']', e);
    }

    try {
      extensionHandler.install(localExtension, namespace);
    } catch (Exception e) {
      // TODO: cleanup

      throw new InstallException("Failed to install extension [" + localExtension.getId() + "]", e);
    }
  }
  /**
   * @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();
    }
  }