/**
  * @param patternMatcher matches a set of elements
  * @param extension the extension to match
  * @return true if one of the element is matched
  */
 public static boolean matches(Pattern patternMatcher, Extension extension) {
   return matches(
       patternMatcher,
       extension.getId().getId(),
       extension.getDescription(),
       extension.getSummary(),
       extension.getName(),
       extension.getFeatures());
 }
  /**
   * 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;
  }
  /**
   * 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;
  }
  /**
   * @param filter the filter
   * @param extension the extension to match
   * @return true if the extension is matched by the filer
   * @since 7.0M2
   */
  public static boolean matches(Filter filter, Extension extension) {
    Object value = extension.get(filter.getField());

    if (value != null) {
      return matches(filter, value);
    }

    // Unknown field
    // FIXME: not sure if it's should be true or false in this case
    return true;
  }
  /**
   * Matches an extension in a case insensitive way.
   *
   * @param patternMatcher the pattern to match
   * @param filters the filters
   * @param extension the extension to match
   * @return true if one of the element is matched
   * @since 7.0M2
   */
  public static boolean matches(
      Pattern patternMatcher, Collection<Filter> filters, Extension extension) {
    if (matches(
        patternMatcher,
        extension.getId().getId(),
        extension.getDescription(),
        extension.getSummary(),
        extension.getName(),
        extension.getFeatures())) {
      for (Filter filter : filters) {
        if (!matches(filter, extension)) {
          return false;
        }
      }

      return true;
    }

    return false;
  }
  /**
   * Indicate if the extension as been installed as a dependency of another one.
   *
   * @param extension the extension
   * @param namespace the namespace to look at, null indicate the root namespace
   * @return true if the the extension has been installed only because it was a dependency of
   *     another extension
   * @see InstalledExtension#isDependency(String)
   * @since 8.2RC1
   */
  public static boolean isDependency(Extension extension, String namespace) {
    boolean isDependency = false;

    if (namespace == null) {
      isDependency = extension.getProperty(PKEY_DEPENDENCY, false);
    } else {
      Object namespacesObject = extension.getProperty(PKEY_NAMESPACES);

      // RETRO-COMPATIBILITY: used to be a String collection with just the actual namespaces
      if (namespacesObject instanceof Map) {
        Map<String, Object> installedNamespace =
            ((Map<String, Map<String, Object>>) namespacesObject).get(namespace);

        isDependency =
            installedNamespace != null
                ? (installedNamespace.get(PKEY_NAMESPACES_DEPENDENCY) == Boolean.TRUE)
                : isDependency(extension, null);
      } else {
        isDependency = isDependency(extension, null);
      }
    }

    return isDependency;
  }
  /**
   * @param extension the extension
   * @return the namespaces in which this extension is enabled. Null means root namespace (i.e all
   *     namespaces).
   */
  public static Collection<String> getNamespaces(Extension extension) {
    Collection<String> namespaces;

    Object namespacesObject = extension.getProperty(PKEY_NAMESPACES);

    // RETRO-COMPATIBILITY: used to be a String collection with just the actual namespaces
    if (namespacesObject == null) {
      namespaces = null;
    } else if (namespacesObject instanceof Collection) {
      namespaces = (Collection<String>) namespacesObject;
    } else {
      namespaces = ((Map<String, Map<String, Object>>) namespacesObject).keySet();
    }

    return namespaces;
  }
  /**
   * @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();
    }
  }
 /**
  * @param extension the extension
  * @return true if the extension is installed
  * @see InstalledExtension#isInstalled()
  */
 public static boolean isInstalled(Extension extension) {
   return extension.getProperty(PKEY_INSTALLED, false);
 }