@Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }

    if (obj == null || !(obj instanceof VersionConstraint)) {
      return false;
    }

    VersionConstraint versionConstraint = (VersionConstraint) obj;

    return this.ranges.equals(versionConstraint.getRanges())
        && ObjectUtils.equals(this.version, versionConstraint.getVersion());
  }
  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;
  }
  /**
   * @param versionConstraint the version constraint to merge
   * @return the version constraint with the upper version or null if both are ranges based
   * @throws IncompatibleVersionConstraintException version constraints are incompatible
   */
  private VersionConstraint mergeVersions(VersionConstraint versionConstraint)
      throws IncompatibleVersionConstraintException {
    if (this.version != null) {
      return mergeVersions(this, versionConstraint);
    } else if (versionConstraint.getVersion() != null) {
      return mergeVersions(versionConstraint, this);
    }

    return null;
  }
  @Override
  public VersionConstraint merge(VersionConstraint versionConstraint)
      throws IncompatibleVersionConstraintException {
    if (equals(versionConstraint)) {
      return this;
    } else {
      VersionConstraint mergedConstraint = mergeVersions(versionConstraint);

      return mergedConstraint == null
          ? mergeRanges(versionConstraint.getRanges())
          : mergedConstraint;
    }
  }
  /**
   * @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;
  }
  /**
   * @param versionConstraintWithVersion the version constraint based on version
   * @param versionConstraint the version constraint for which we don't know yet if it's based on
   *     version or ranges
   * @return the merged version constraint
   * @throws IncompatibleVersionConstraintException version constraints are incompatible
   */
  private VersionConstraint mergeVersions(
      VersionConstraint versionConstraintWithVersion, VersionConstraint versionConstraint)
      throws IncompatibleVersionConstraintException {
    if (versionConstraint.getVersion() != null) {
      return versionConstraintWithVersion.getVersion().compareTo(versionConstraint.getVersion())
              >= 0
          ? versionConstraintWithVersion
          : versionConstraint;
    } else {
      if (!versionConstraint.containsVersion(versionConstraintWithVersion.getVersion())) {
        throw new IncompatibleVersionConstraintException(
            "Version ["
                + versionConstraintWithVersion.getVersion()
                + "] is not part of version constraint ["
                + versionConstraint
                + "]");
      }

      return versionConstraintWithVersion;
    }
  }
  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;
  }
 /**
  * Created a new {@link DefaultVersionConstraint} by cloning the provided version constraint.
  *
  * @param versionConstraint the version constrain to copy
  */
 public DefaultVersionConstraint(VersionConstraint versionConstraint) {
   this(versionConstraint.getRanges(), versionConstraint.getVersion());
 }