All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sirix.diff.JsonDiffSerializer Maven / Gradle / Ivy

Go to download

SirixDB is a hybrid on-disk and in-memory document oriented, versioned database system. It has a lightweight buffer manager, stores everything in a huge persistent and durable tree and allows efficient reconstruction of every revision. Furthermore, SirixDB implements change tracking, diffing and supports time travel queries.

There is a newer version: 0.11.0
Show newest version
package io.sirix.diff;

import com.google.api.client.util.Objects;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.sirix.api.json.JsonNodeReadOnlyTrx;
import io.sirix.api.json.JsonResourceSession;
import io.sirix.node.NodeKind;
import io.sirix.service.json.serialize.JsonSerializer;

import java.io.IOException;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.util.Collection;

public final class JsonDiffSerializer {

  private final String databaseName;
  private final JsonResourceSession resourceManager;
  private final int oldRevisionNumber;
  private final int newRevisionNumber;
  private final Collection diffs;

  public JsonDiffSerializer(final String databaseName,
                            JsonResourceSession resourceManager,
                            int oldRevisionNumber,
                            int newRevisionNumber,
                            Collection diffs) {
    this.databaseName = databaseName;
    this.resourceManager = resourceManager;
    this.oldRevisionNumber = oldRevisionNumber;
    this.newRevisionNumber = newRevisionNumber;
    this.diffs = diffs;
  }

  public String serialize(boolean emitFromDiffAlgorithm) {
    final var resourceName = resourceManager.getResourceConfig().getName();

    final var json = createMetaInfo(databaseName, resourceName, oldRevisionNumber, newRevisionNumber);

    if (diffs.size() == 1) {
      final var tuple = diffs.iterator().next();
      if (tuple.getDiff() == DiffFactory.DiffType.SAME || tuple.getDiff() == DiffFactory.DiffType.SAMEHASH) {
        return json.toString();
      }
    }

    final var jsonDiffs = json.getAsJsonArray("diffs");

    try (final var oldRtx = resourceManager.beginNodeReadOnlyTrx(oldRevisionNumber);
         final var newRtx = resourceManager.beginNodeReadOnlyTrx(newRevisionNumber)) {
      if (emitFromDiffAlgorithm) {
        diffs.removeIf(diffTuple -> diffTuple.getDiff() == DiffFactory.DiffType.SAME
            || diffTuple.getDiff() == DiffFactory.DiffType.SAMEHASH
            || diffTuple.getDiff() == DiffFactory.DiffType.REPLACEDOLD);
      }

      if (diffs.isEmpty()) {
        return json.toString();
      }

      for (final var diffTuple : diffs) {
        final var diffType = diffTuple.getDiff();

        if (diffType == DiffFactory.DiffType.INSERTED) {
          newRtx.moveTo(diffTuple.getNewNodeKey());
        } else if (diffType == DiffFactory.DiffType.DELETED) {
          oldRtx.moveTo(diffTuple.getOldNodeKey());
        } else {
          newRtx.moveTo(diffTuple.getNewNodeKey());
          oldRtx.moveTo(diffTuple.getOldNodeKey());
        }

        switch (diffType) {
          case INSERTED:
            final var insertedJson = new JsonObject();
            final var jsonInsertDiff = new JsonObject();

            insertBasedOnNewRtx(newRtx, jsonInsertDiff);

            if (resourceManager.getResourceConfig().areDeweyIDsStored) {
              final var deweyId = newRtx.getDeweyID();
              jsonInsertDiff.addProperty("deweyID", deweyId.toString());
              jsonInsertDiff.addProperty("depth", deweyId.getLevel());
            }

            addTypeAndDataProperties(newRtx, jsonInsertDiff, newRevisionNumber, emitFromDiffAlgorithm);

            insertedJson.add("insert", jsonInsertDiff);
            jsonDiffs.add(insertedJson);

            break;
          case DELETED:
            final var deletedJson = new JsonObject();

            if (resourceManager.getResourceConfig().areDeweyIDsStored) {
              final var jsonDeletedDiff = new JsonObject();

              jsonDeletedDiff.addProperty("nodeKey", diffTuple.getOldNodeKey());

              final var deweyId = oldRtx.getDeweyID();
              jsonDeletedDiff.addProperty("deweyID", deweyId.toString());
              jsonDeletedDiff.addProperty("depth", deweyId.getLevel());

              deletedJson.add("delete", jsonDeletedDiff);
            } else {
              deletedJson.addProperty("delete", diffTuple.getOldNodeKey());
            }

            jsonDiffs.add(deletedJson);
            break;
          case REPLACEDNEW:
            final var replaceJson = new JsonObject();
            final var jsonReplaceDiff = new JsonObject();

            replaceJson.add("replace", jsonReplaceDiff);

            jsonReplaceDiff.addProperty("oldNodeKey", diffTuple.getOldNodeKey());
            jsonReplaceDiff.addProperty("newNodeKey", diffTuple.getNewNodeKey());

            if (resourceManager.getResourceConfig().areDeweyIDsStored) {
              final var deweyId = newRtx.getDeweyID();
              jsonReplaceDiff.addProperty("deweyID", deweyId.toString());
              jsonReplaceDiff.addProperty("depth", deweyId.getLevel());
            }

            addTypeAndDataProperties(newRtx, jsonReplaceDiff, newRevisionNumber, emitFromDiffAlgorithm);

            jsonDiffs.add(replaceJson);
            break;
          case UPDATED:
            final var updateJson = new JsonObject();
            final var jsonUpdateDiff = new JsonObject();

            jsonUpdateDiff.addProperty("nodeKey", diffTuple.getOldNodeKey());

            if (resourceManager.getResourceConfig().areDeweyIDsStored) {
              final var deweyId = newRtx.getDeweyID();
              jsonUpdateDiff.addProperty("deweyID", deweyId.toString());
              jsonUpdateDiff.addProperty("depth", deweyId.getLevel());
            }

            if (!Objects.equal(oldRtx.getName(), newRtx.getName())) {
              jsonUpdateDiff.addProperty("name", newRtx.getName().toString());
            } else if (!Objects.equal(oldRtx.getValue(), newRtx.getValue())) {
              if (newRtx.getKind() == NodeKind.BOOLEAN_VALUE || newRtx.getKind() == NodeKind.OBJECT_BOOLEAN_VALUE) {
                jsonUpdateDiff.addProperty("type", "boolean");
                jsonUpdateDiff.addProperty("value", newRtx.getBooleanValue());
              } else if (newRtx.getKind() == NodeKind.STRING_VALUE
                  || newRtx.getKind() == NodeKind.OBJECT_STRING_VALUE) {
                jsonUpdateDiff.addProperty("type", "string");
                jsonUpdateDiff.addProperty("value", newRtx.getValue());
              } else if (newRtx.getKind() == NodeKind.NULL_VALUE || newRtx.getKind() == NodeKind.OBJECT_NULL_VALUE) {
                jsonUpdateDiff.addProperty("type", "null");
                jsonUpdateDiff.add("value", null);
              } else if (newRtx.getKind() == NodeKind.NUMBER_VALUE
                  || newRtx.getKind() == NodeKind.OBJECT_NUMBER_VALUE) {
                jsonUpdateDiff.addProperty("type", "number");
                jsonUpdateDiff.addProperty("value", newRtx.getNumberValue());
              }
            }

            updateJson.add("update", jsonUpdateDiff);
            jsonDiffs.add(updateJson);

            // $CASES-OMITTED$
          default:
            // Do nothing.
        }
      }
    }

    return json.toString();
  }

  private void insertBasedOnNewRtx(JsonNodeReadOnlyTrx newRtx, JsonObject jsonInsertDiff) {
    jsonInsertDiff.addProperty("nodeKey", newRtx.getNodeKey());
    final var insertPosition = newRtx.hasLeftSibling() ? "asRightSibling" : "asFirstChild";

    jsonInsertDiff.addProperty("insertPositionNodeKey",
                               newRtx.hasLeftSibling() ? newRtx.getLeftSiblingKey() : newRtx.getParentKey());
    jsonInsertDiff.addProperty("insertPosition", insertPosition);
  }

  private JsonObject createMetaInfo(final String databaseName, final String resourceName, final int oldRevision,
      final int newRevision) {
    final var json = new JsonObject();
    json.addProperty("database", databaseName);
    json.addProperty("resource", resourceName);
    json.addProperty("old-revision", oldRevision);
    json.addProperty("new-revision", newRevision);
    final var diffsArray = new JsonArray();
    json.add("diffs", diffsArray);
    return json;
  }

  private void addTypeAndDataProperties(JsonNodeReadOnlyTrx newRtx, JsonObject json, int newRevisionNumber,
      boolean emitFromDiffAlgorithm) {
    if (newRtx.isArray() || newRtx.isObject() || newRtx.isObjectKey()) {
      json.addProperty("type", "jsonFragment");
      if (emitFromDiffAlgorithm) {
        serialize(newRevisionNumber, resourceManager, newRtx, json);
      }
    } else if (newRtx.getKind() == NodeKind.BOOLEAN_VALUE || newRtx.getKind() == NodeKind.OBJECT_BOOLEAN_VALUE) {
      json.addProperty("type", "boolean");
      json.addProperty("data", newRtx.getBooleanValue());
    } else if (newRtx.getKind() == NodeKind.STRING_VALUE || newRtx.getKind() == NodeKind.OBJECT_STRING_VALUE) {
      json.addProperty("type", "string");
      json.addProperty("data", newRtx.getValue());
    } else if (newRtx.getKind() == NodeKind.NULL_VALUE || newRtx.getKind() == NodeKind.OBJECT_NULL_VALUE) {
      json.addProperty("type", "null");
      json.add("data", null);
    } else if (newRtx.getKind() == NodeKind.NUMBER_VALUE || newRtx.getKind() == NodeKind.OBJECT_NUMBER_VALUE) {
      json.addProperty("type", "number");
      json.addProperty("data", newRtx.getNumberValue());
    }
  }

  public static void serialize(int newRevision, JsonResourceSession resourceManager, JsonNodeReadOnlyTrx newRtx,
      JsonObject jsonObject) {
    try (final var writer = new StringWriter()) {
      final var serializer =
          JsonSerializer.newBuilder(resourceManager, writer, newRevision).startNodeKey(newRtx.getNodeKey()).build();
      serializer.call();
      jsonObject.addProperty("data", writer.toString());
    } catch (final IOException e) {
      throw new UncheckedIOException(e);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy