@Override
 public Extension resolve(ExtensionDependency extensionDependency) throws ResolveException {
   return resolve(
       new ExtensionId(
           extensionDependency.getId(),
           new DefaultVersion(extensionDependency.getVersionConstraint().getValue())));
 }
  /**
   * 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;
  }
  /**
   * Install provided extension dependency.
   *
   * @param extensionDependency the extension dependency to install
   * @param namespace the namespace where to install the extension
   * @param parentBranch the children of the parent {@link DefaultExtensionPlanNode}
   * @throws InstallException error when trying to install provided extension
   */
  private void installExtensionDependency(
      ExtensionDependency extensionDependency,
      String namespace,
      List<ModifableExtensionPlanNode> parentBranch)
      throws InstallException {
    if (getRequest().isVerbose()) {
      if (namespace != null) {
        this.logger.info(
            LOG_RESOLVEDEPENDENCY_NAMESPACE,
            "Resolving extension dependency [{}] on namespace [{}]",
            extensionDependency,
            namespace);
      } else {
        this.logger.info(
            LOG_RESOLVEDEPENDENCY,
            "Resolving extension dependency [{}] on all namespaces",
            extensionDependency);
      }
    }

    VersionConstraint versionConstraint = extensionDependency.getVersionConstraint();

    // Make sure the dependency is not already a core extension
    if (checkCoreExtension(extensionDependency, parentBranch)) {
      // Already exists and added to the tree by #checkCoreExtension
      return;
    }

    // Make sure the dependency is not already in the current plan
    versionConstraint =
        checkExistingPlanNode(extensionDependency, namespace, parentBranch, versionConstraint);
    if (versionConstraint == null) {
      // Already exists and added to the tree by #checkExistingPlan
      return;
    }

    // Check installed extensions
    InstalledExtension previousExtension =
        this.installedExtensionRepository.getInstalledExtension(
            extensionDependency.getId(), namespace);
    ExtensionDependency targetDependency =
        checkInstalledExtension(
            previousExtension, extensionDependency, versionConstraint, namespace, parentBranch);
    if (targetDependency == null) {
      // Already exists and added to the tree by #checkInstalledExtension
      return;
    }

    // Not found locally, search it remotely
    ModifableExtensionPlanNode node =
        installExtension(previousExtension, targetDependency, true, namespace);

    node.versionConstraint = versionConstraint;

    addExtensionNode(node);
    parentBranch.add(node);
  }
  /**
   * 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;
  }
  private boolean checkCoreExtension(
      ExtensionDependency extensionDependency, List<ModifableExtensionPlanNode> parentBranch)
      throws InstallException {
    CoreExtension coreExtension =
        this.coreExtensionRepository.getCoreExtension(extensionDependency.getId());

    if (coreExtension != null) {
      if (!extensionDependency
          .getVersionConstraint()
          .isCompatible(coreExtension.getId().getVersion())) {
        throw new InstallException(
            "Dependency ["
                + extensionDependency
                + "] is not compatible with core extension ["
                + coreExtension
                + "]");
      } else {
        if (getRequest().isVerbose()) {
          this.logger.debug(
              "There is already a core extension [{}] covering extension dependency [{}]",
              coreExtension.getId(),
              extensionDependency);
        }

        ModifableExtensionPlanNode node =
            new ModifableExtensionPlanNode(
                extensionDependency, extensionDependency.getVersionConstraint());
        node.setAction(
            new DefaultExtensionPlanAction(coreExtension, null, Action.NONE, null, true));

        parentBranch.add(node);

        return true;
      }
    }

    return false;
  }
  /**
   * @param extensions the extensions containing the dependencies for which to merge the constraints
   * @param dependencyId the id of the dependency
   * @param previousMergedVersionContraint if not null it's merged with the provided extension
   *     dependencies version constraints
   * @return the merged version constraint
   * @throws IncompatibleVersionConstraintException the provided version constraint is compatible
   *     with the provided version constraint
   */
  private VersionConstraint mergeVersionConstraints(
      Collection<? extends Extension> extensions,
      String dependencyId,
      VersionConstraint previousMergedVersionContraint)
      throws IncompatibleVersionConstraintException {
    VersionConstraint mergedVersionContraint = previousMergedVersionContraint;

    if (extensions != null) {
      for (Extension extension : extensions) {
        ExtensionDependency dependency = getDependency(extension, dependencyId);

        if (dependency != null) {
          if (mergedVersionContraint == null) {
            mergedVersionContraint = dependency.getVersionConstraint();
          } else {
            mergedVersionContraint =
                mergedVersionContraint.merge(dependency.getVersionConstraint());
          }
        }
      }
    }

    return mergedVersionContraint;
  }
  private VersionConstraint checkExistingPlanNode(
      ExtensionDependency extensionDependency,
      String namespace,
      List<ModifableExtensionPlanNode> parentBranch,
      VersionConstraint previousVersionConstraint)
      throws InstallException {
    VersionConstraint versionConstraint = previousVersionConstraint;

    ModifableExtensionPlanNode existingNode =
        getExtensionNode(extensionDependency.getId(), namespace);
    if (existingNode != null) {
      if (versionConstraint.isCompatible(
          existingNode.getAction().getExtension().getId().getVersion())) {
        ModifableExtensionPlanNode node =
            new ModifableExtensionPlanNode(extensionDependency, existingNode);
        addExtensionNode(node);
        parentBranch.add(node);

        return null;
      } else {
        if (existingNode.versionConstraint != null) {
          try {
            versionConstraint = versionConstraint.merge(existingNode.versionConstraint);
          } catch (IncompatibleVersionConstraintException e) {
            throw new InstallException(
                "Dependency ["
                    + extensionDependency
                    + "] is incompatible with current containt ["
                    + existingNode.versionConstraint
                    + "]",
                e);
          }
        } else {
          throw new InstallException(
              "Dependency ["
                  + extensionDependency
                  + "] incompatible with extension ["
                  + existingNode.getAction().getExtension()
                  + "]");
        }
      }
    }

    return versionConstraint;
  }
  private void guess(
      Map<String, DefaultCoreExtension> extensions, DefaultCoreExtensionRepository repository) {
    Set<ExtensionDependency> dependencies = new HashSet<ExtensionDependency>();

    for (DefaultCoreExtension coreExtension : extensions.values()) {
      for (ExtensionDependency dependency : coreExtension.getDependencies()) {
        dependencies.add(dependency);
      }
    }

    // Normalize and guess

    Map<String, Object[]> fileNames = new HashMap<String, Object[]>();
    Map<String, Object[]> guessedArtefacts = new HashMap<String, Object[]>();
    Set<URL> urls = ClasspathHelper.forClassLoader();

    for (URL url : urls) {
      try {
        String path = url.toURI().getPath();
        String filename = path.substring(path.lastIndexOf('/') + 1);
        String type = null;

        int extIndex = filename.lastIndexOf('.');
        if (extIndex != -1) {
          type = filename.substring(extIndex + 1);
          filename = filename.substring(0, extIndex);
        }

        int index;
        if (!filename.endsWith(SNAPSHOTSUFFIX)) {
          index = filename.lastIndexOf('-');
        } else {
          index = filename.lastIndexOf('-', filename.length() - SNAPSHOTSUFFIX.length());
        }

        if (index != -1) {
          fileNames.put(filename, new Object[] {url});

          String artefactname = filename.substring(0, index);
          String version = filename.substring(index + 1);

          guessedArtefacts.put(artefactname, new Object[] {version, url, type});
        }
      } catch (Exception e) {
        this.logger.warn("Failed to parse resource name [" + url + "]", e);
      }
    }

    // Try to resolve version no easy to find from the pom.xml
    try {
      for (DefaultCoreExtension coreExtension : extensions.values()) {
        String artifactId = getArtifactId(coreExtension);

        Object[] artefact = guessedArtefacts.get(artifactId);

        if (artefact != null) {
          if (coreExtension.getId().getVersion().getValue().charAt(0) == '$') {
            coreExtension.setId(
                new ExtensionId(coreExtension.getId().getId(), (String) artefact[0]));
            coreExtension.setGuessed(true);
          }

          if (coreExtension.getType().charAt(0) == '$') {
            coreExtension.setType((String) artefact[2]);
            coreExtension.setGuessed(true);
          }
        }
      }

      // Add dependencies that does not provide proper pom.xml resource and can't be found in the
      // classpath
      for (ExtensionDependency extensionDependency : dependencies) {
        Dependency dependency =
            (Dependency)
                extensionDependency.getProperty(MavenCoreExtensionDependency.PKEY_MAVEN_DEPENDENCY);

        if (dependency == null) {
          dependency =
              toDependency(
                  extensionDependency.getId(),
                  extensionDependency.getVersionConstraint().getValue(),
                  null);
        }

        String dependencyId = dependency.getGroupId() + ':' + dependency.getArtifactId();

        DefaultCoreExtension coreExtension = extensions.get(dependencyId);
        if (coreExtension == null) {
          String dependencyFileName = dependency.getArtifactId() + '-' + dependency.getVersion();
          if (dependency.getClassifier() != null) {
            dependencyFileName += '-' + dependency.getClassifier();
            dependencyId += ':' + dependency.getClassifier();
          }

          Object[] filenameArtifact = fileNames.get(dependencyFileName);
          Object[] guessedArtefact = guessedArtefacts.get(dependency.getArtifactId());

          if (filenameArtifact != null) {
            coreExtension =
                new DefaultCoreExtension(
                    repository,
                    (URL) filenameArtifact[0],
                    new ExtensionId(dependencyId, dependency.getVersion()),
                    packagingToType(dependency.getType()));
            coreExtension.setGuessed(true);
          } else if (guessedArtefact != null) {
            coreExtension =
                new DefaultCoreExtension(
                    repository,
                    (URL) guessedArtefact[1],
                    new ExtensionId(dependencyId, (String) guessedArtefact[0]),
                    packagingToType(dependency.getType()));
            coreExtension.setGuessed(true);
          }

          if (coreExtension != null) {
            extensions.put(dependencyId, coreExtension);
          }
        }
      }
    } catch (Exception e) {
      this.logger.warn("Failed to guess extra information about some extensions", 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();
    }
  }
  private ExtensionDependency checkInstalledExtension(
      InstalledExtension installedExtension,
      ExtensionDependency extensionDependency,
      VersionConstraint versionConstraint,
      String namespace,
      List<ModifableExtensionPlanNode> parentBranch)
      throws InstallException {
    ExtensionDependency targetDependency = extensionDependency;

    if (installedExtension != null) {
      // Check if already installed version is compatible
      if (installedExtension.isValid(namespace)
          && versionConstraint.isCompatible(installedExtension.getId().getVersion())) {
        if (getRequest().isVerbose()) {
          this.logger.debug(
              "There is already an installed extension [{}] covering extension dependency [{}]",
              installedExtension.getId(),
              extensionDependency);
        }

        ModifableExtensionPlanNode node =
            new ModifableExtensionPlanNode(extensionDependency, versionConstraint);
        node.setAction(
            new DefaultExtensionPlanAction(
                installedExtension,
                null,
                Action.NONE,
                namespace,
                installedExtension.isDependency(namespace)));

        addExtensionNode(node);
        parentBranch.add(node);

        return null;
      }

      // If not compatible with it, try to merge dependencies constraint of all backward
      // dependencies to find a
      // new compatible version for this extension
      VersionConstraint mergedVersionContraint;
      try {
        if (installedExtension.isInstalled(null)) {
          Map<String, Collection<InstalledExtension>> backwardDependencies =
              this.installedExtensionRepository.getBackwardDependencies(installedExtension.getId());

          mergedVersionContraint =
              mergeVersionConstraints(
                  backwardDependencies.get(null), extensionDependency.getId(), versionConstraint);
          if (namespace != null) {
            mergedVersionContraint =
                mergeVersionConstraints(
                    backwardDependencies.get(namespace),
                    extensionDependency.getId(),
                    mergedVersionContraint);
          }
        } else {
          Collection<InstalledExtension> backwardDependencies =
              this.installedExtensionRepository.getBackwardDependencies(
                  installedExtension.getId().getId(), namespace);

          mergedVersionContraint =
              mergeVersionConstraints(
                  backwardDependencies, extensionDependency.getId(), versionConstraint);
        }
      } catch (IncompatibleVersionConstraintException e) {
        throw new InstallException(
            "Provided depency is incompatible with already installed extensions", e);
      } catch (ResolveException e) {
        throw new InstallException("Failed to resolve backward dependencies", e);
      }

      if (mergedVersionContraint != versionConstraint) {
        targetDependency =
            new DefaultExtensionDependency(extensionDependency, mergedVersionContraint);
      }
    }

    return targetDependency;
  }