/**
   * Create a patch representing what we actually processed. This may contain some fixed content
   * hashes for removed modules.
   *
   * @param original the original
   * @return the processed patch
   */
  protected Patch createProcessedPatch(final Patch original) {

    // Process elements
    final List<PatchElement> elements = new ArrayList<PatchElement>();
    // Process layers
    for (final PatchEntry entry : getLayers()) {
      final PatchElement element =
          createPatchElement(entry, entry.element.getId(), entry.modifications);
      elements.add(element);
    }
    // Process add-ons
    for (final PatchEntry entry : getAddOns()) {
      final PatchElement element =
          createPatchElement(entry, entry.element.getId(), entry.modifications);
      elements.add(element);
    }

    // Swap the patch element modifications, keep the identity ones since we don't need to fix the
    // misc modifications
    return new PatchImpl(
        original.getPatchId(),
        original.getDescription(),
        original.getIdentity(),
        elements,
        original.getModifications());
  }
  /**
   * Finalize the patch.
   *
   * @param callback the finalize callback
   * @return the result
   * @throws Exception
   */
  protected PatchingResult finalize(final FinalizeCallback callback) throws Exception {
    assert state == State.NEW;
    final Patch original = callback.getPatch();
    final Patch.PatchType patchType = original.getIdentity().getPatchType();
    final String patchId;
    if (patchType == Patch.PatchType.CUMULATIVE) {
      patchId = modification.getCumulativePatchID();
    } else {
      patchId = original.getPatchId();
    }
    try {
      // The processed patch, based on the recorded changes
      final Patch processedPatch = createProcessedPatch(original);
      // The rollback containing all the recorded rollback actions
      final RollbackPatch rollbackPatch = createRollbackPatch(patchId, patchType);
      callback.finishPatch(processedPatch, rollbackPatch, this);
    } catch (Exception e) {
      if (undoChanges()) {
        callback.operationCancelled(this);
      }
      throw e;
    }
    state = State.PREPARED;
    return new PatchingResult() {
      @Override
      public String getPatchId() {
        return original.getPatchId();
      }

      @Override
      public PatchInfo getPatchInfo() {
        return new PatchInfo() {
          @Override
          public String getVersion() {
            return identityEntry.getResultingVersion();
          }

          @Override
          public String getCumulativePatchID() {
            return identityEntry.delegate.getModifiedState().getCumulativePatchID();
          }

          @Override
          public List<String> getPatchIDs() {
            return identityEntry.delegate.getModifiedState().getPatchIDs();
          }
        };
      }

      @Override
      public void commit() {
        if (state == State.PREPARED) {
          complete(modification, callback);
        } else {
          undoChanges();
          throw new IllegalStateException();
        }
      }

      @Override
      public void rollback() {
        if (undoChanges()) {
          try {
            callback.operationCancelled(IdentityPatchContext.this);
          } finally {
            modification.cancel();
          }
        }
      }
    };
  }