/**
   * Install provided extension.
   *
   * @param extensionId the identifier of the extension to install
   * @param dependency indicate if the extension is installed as a dependency
   * @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
   * @throws ResolveException unexpected exception has been raised
   */
  protected void installExtension(
      ExtensionId extensionId,
      boolean dependency,
      String namespace,
      ModifableExtensionPlanTree parentBranch)
      throws InstallException, ResolveException {
    if (getRequest().isVerbose()) {
      if (namespace != null) {
        this.logger.info(
            LOG_RESOLVE_NAMESPACE,
            "Resolving extension [{}] on namespace [{}]",
            extensionId,
            namespace);
      } else {
        this.logger.info(LOG_RESOLVE, "Resolving extension [{}] on all namespaces", extensionId);
      }
    }

    // Make sure the extension is not already a core extension
    if (this.coreExtensionRepository.exists(extensionId.getId())) {
      throw new InstallException(
          String.format("There is already a core extension with the id [%s]", extensionId.getId()));
    }

    InstalledExtension previousExtension =
        checkAlreadyInstalledExtension(extensionId.getId(), extensionId.getVersion(), namespace);

    ModifableExtensionPlanNode node =
        installExtension(previousExtension, extensionId, dependency, namespace);

    addExtensionNode(node);
    parentBranch.add(node);
  }
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof ExtensionId) {
      ExtensionId extensionId = (ExtensionId) obj;

      return getId().equals(extensionId.getId()) && getVersion().equals(extensionId.getVersion());
    }

    return false;
  }
  /**
   * Start the asynchronous uninstall process for an extension if the context document has
   * programming rights and no other job is in progress already.
   *
   * <p>Uninstall from all namepspace.
   *
   * @param extensionId the identifier of the extension to remove
   * @return the {@link Job} object which can be used to monitor the progress of the uninstallation
   *     process, or {@code null} in case of failure
   */
  public Job uninstall(ExtensionId extensionId) {
    if (!this.documentAccessBridge.hasProgrammingRights()) {
      setError(new JobException("Need programming right to uninstall an extension"));

      return null;
    }

    setError(null);

    UninstallRequest uninstallRequest = new UninstallRequest();
    uninstallRequest.setId(extensionId.getId());
    uninstallRequest.addExtension(extensionId);

    Job job;
    try {
      job = this.jobManager.executeJob(UninstallJob.JOBTYPE, uninstallRequest);
    } catch (Exception e) {
      setError(e);

      job = null;
    }

    return job;
  }
 /**
  * @param extensionId the event related extension identifier
  * @return <code>true</code> if the passed event matches this event, <code>false</code> otherwise.
  */
 private boolean macthesExtensionId(ExtensionId extensionId) {
   return this.extensionId.equals(extensionId)
       || (this.extensionId.getVersion() == null
           && this.extensionId.getId().equals(extensionId.getId()));
 }
 String getPathSuffix(ExtensionId extensionId, String type) {
   return extensionId.getId() + '-' + extensionId.getVersion().getValue() + '.' + type;
 }