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

io.sirix.service.json.shredder.JsonItemShredder 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.service.json.shredder;

import com.google.gson.stream.JsonReader;
import io.sirix.access.trx.node.json.objectvalue.*;
import io.sirix.api.json.JsonNodeTrx;
import io.sirix.exception.SirixException;
import io.sirix.node.NodeKind;
import io.sirix.service.InsertPosition;
import io.sirix.service.ShredderCommit;
import io.sirix.service.json.JsonNumber;
import io.sirix.settings.Fixed;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongStack;
import io.brackit.query.atomic.Atomic;
import io.brackit.query.atomic.Numeric;
import io.brackit.query.jdm.Item;
import io.brackit.query.jdm.Sequence;
import io.brackit.query.jdm.Type;
import io.brackit.query.jdm.json.Array;
import io.brackit.query.jdm.json.Object;

import java.util.concurrent.Callable;

import static java.util.Objects.requireNonNull;

/**
 * This class appends a given {@link JsonReader} to a {@link JsonNodeTrx} . The content of the
 * stream is added as a subtree. Based on an enum which identifies the point of insertion, the
 * subtree is either added as first child or as right sibling.
 *
 * @author Johannes Lichtenberger, University of Konstanz
 */
public final class JsonItemShredder implements Callable {

  /**
   * {@link JsonNodeTrx}.
   */
  private final JsonNodeTrx wtx;

  /**
   * {@link Item} implementation.
   */
  private final Item item;

  /**
   * Determines if changes are going to be commit right after shredding.
   */
  private final ShredderCommit commit;

  /**
   * Keeps track of visited keys.
   */
  private final LongStack parents;

  /**
   * Insertion position.
   */
  private InsertPosition insert;

  private int level;

  private final boolean skipRootJson;

  /**
   * Builder to build an {@link JsonItemShredder} instance.
   */
  public static class Builder {

    /**
     * {@link JsonNodeTrx} implementation.
     */
    private final JsonNodeTrx wtx;

    /**
     * {@link Item} implementation.
     */
    private final Item item;

    /**
     * Insertion position.
     */
    private final InsertPosition insert;

    /**
     * Determines if after shredding the transaction should be immediately commited.
     */
    private ShredderCommit commit = ShredderCommit.NOCOMMIT;

    private boolean skipRootJsonToken;

    /**
     * Constructor.
     *
     * @param wtx    {@link JsonNodeTrx} implementation
     * @param item   {@link Item} implementation
     * @param insert insertion position
     * @throws NullPointerException if one of the arguments is {@code null}
     */
    public Builder(final JsonNodeTrx wtx, final Item item, final InsertPosition insert) {
      this.wtx = requireNonNull(wtx);
      this.item = requireNonNull(item);
      this.insert = requireNonNull(insert);
    }

    /**
     * Commit afterwards.
     *
     * @return this builder instance
     */
    public Builder commitAfterwards() {
      commit = ShredderCommit.COMMIT;
      return this;
    }

    public Builder skipRootJsonToken() {
      skipRootJsonToken = true;
      return this;
    }

    /**
     * Build an instance.
     *
     * @return {@link JsonItemShredder} instance
     */
    public JsonItemShredder build() {
      return new JsonItemShredder(this);
    }
  }

  /**
   * Private constructor.
   *
   * @param builder builder reference
   */
  private JsonItemShredder(final Builder builder) {
    wtx = builder.wtx;
    item = builder.item;
    insert = builder.insert;
    commit = builder.commit;
    skipRootJson = builder.skipRootJsonToken;

    parents = new LongArrayList();
    parents.push(Fixed.NULL_NODE_KEY.getStandardProperty());
  }

  /**
   * Invoking the shredder.
   *
   * @return revision of file
   * @throws SirixException if any kind of sirix exception which has occured
   */
  @Override
  public Long call() {
    final long revision = wtx.getRevisionNumber();
    insertNewContent();
    commit.commit(wtx);
    return revision;
  }

  private void json(Sequence parent, Sequence sequence, String objectField,
      boolean nextTokenIsParent) {
    switch (sequence) {
      case Atomic atomic -> {
        if (sequence instanceof Numeric) {
          final var number = JsonNumber.stringToNumber(sequence.toString());

          if (objectField != null) {
            final var value = new NumberValue(number);
            addObjectRecord(objectField, value, nextTokenIsParent);
          } else {
            insertNumberValue(number, nextTokenIsParent);
          }
        } else if (((Atomic) sequence).type() == Type.BOOL) {
          final var bool = sequence.booleanValue();

          if (objectField != null) {
            final var value = new BooleanValue(bool);
            addObjectRecord(objectField, value, nextTokenIsParent);
          } else {
            insertBooleanValue(bool, nextTokenIsParent);
          }
        } else if (((Atomic) sequence).type() == Type.NULL) {
          if (objectField != null) {
            final var value = new NullValue();
            addObjectRecord(objectField, value, nextTokenIsParent);
          } else {
            insertNullValue(nextTokenIsParent);
          }
        } else {
          final var str = ((Atomic) sequence).asStr().stringValue();

          if (objectField != null) {
            final var value = new StringValue(str);
            addObjectRecord(objectField, value, nextTokenIsParent);
          } else {
            insertStringValue(str, nextTokenIsParent);
          }
        }
      }
      case Array a -> {
        level++;
        if (!(level == 1 && skipRootJson)) {
          if (objectField != null) {
            final var value = new ArrayValue();
            addObjectRecord(objectField, value, nextTokenIsParent);
          } else {
            insertArray();
          }
        }
        for (int i = 0; i < a.len(); i++) {
          final Sequence seq = a.at(i);
          json(a, seq, null, false);
        }
        level--;
        if (!(level == 0 && skipRootJson)) {
          parents.popLong();
          wtx.moveTo(parents.peekLong(0));

          if (parent instanceof Object) {
            parents.popLong();
            wtx.moveTo(parents.peekLong(0));
          }
        }
      }
      case Object object -> {
        level++;
        if (!(level == 1 && skipRootJson)) {
          if (objectField != null) {
            final var value = new ObjectValue();
            addObjectRecord(objectField, value, nextTokenIsParent);
          } else {
            addObject();
          }
        }
        for (int i = 0; i < object.len(); i++) {
          final var value = object.value(i);
          json(object,
               value,
               object.name(i).stringValue(),
               i + 1 == object.len() || !(value instanceof Array) && !(value instanceof Object));
        }
        level--;
        if (!(level == 0 && skipRootJson)) {
          parents.popLong();
          wtx.moveTo(parents.peekLong(0));

          if (parent instanceof Object) {
            parents.popLong();
            wtx.moveTo(parents.peekLong(0));
          }
        }
      }
      default -> throw new IllegalStateException("Unexpected value: " + sequence);
    }
  }

  /**
   * Insert new content based on a StAX like parser.
   *
   * @throws SirixException if something went wrong while inserting
   */
  private void insertNewContent() {
    level = 0;

    json(null, item, null, false);
  }

  private long insertStringValue(final String stringValue, final boolean nextTokenIsParent) {
    final String value = requireNonNull(stringValue);
    final long key;

    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertStringValueAsFirstChild(value).getNodeKey();
        } else {
          key = wtx.insertStringValueAsRightSibling(value).getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertStringValueAsLastChild(value).getNodeKey();
        } else {
          key = wtx.insertStringValueAsRightSibling(value).getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> key = wtx.insertStringValueAsLeftSibling(value).getNodeKey();
      case AS_RIGHT_SIBLING -> key = wtx.insertStringValueAsRightSibling(value).getNodeKey();
      default -> throw new AssertionError();//Should not happen
    }

    adaptTrxPosAndStack(nextTokenIsParent, key);

    return key;
  }

  private long insertBooleanValue(final boolean boolValue, final boolean nextTokenIsParent) {
    final long key;

    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertBooleanValueAsFirstChild(boolValue).getNodeKey();
        } else {
          key = wtx.insertBooleanValueAsRightSibling(boolValue).getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertBooleanValueAsLastChild(boolValue).getNodeKey();
        } else {
          key = wtx.insertBooleanValueAsRightSibling(boolValue).getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> key = wtx.insertBooleanValueAsLeftSibling(boolValue).getNodeKey();
      case AS_RIGHT_SIBLING -> key = wtx.insertBooleanValueAsRightSibling(boolValue).getNodeKey();
      default -> throw new AssertionError();//Should not happen
    }

    adaptTrxPosAndStack(nextTokenIsParent, key);

    return key;
  }

  private long insertNumberValue(final Number numberValue, final boolean nextTokenIsParent) {
    final Number value = requireNonNull(numberValue);

    final long key;

    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertNumberValueAsFirstChild(value).getNodeKey();
        } else {
          key = wtx.insertNumberValueAsRightSibling(value).getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertNumberValueAsLastChild(value).getNodeKey();
        } else {
          key = wtx.insertNumberValueAsRightSibling(value).getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> key = wtx.insertNumberValueAsLeftSibling(value).getNodeKey();
      case AS_RIGHT_SIBLING -> key = wtx.insertNumberValueAsRightSibling(value).getNodeKey();
      default -> throw new AssertionError();//Should not happen
    }

    adaptTrxPosAndStack(nextTokenIsParent, key);

    return key;
  }

  private void adaptTrxPosAndStack(final boolean nextTokenIsParent, final long key) {
    parents.popLong();

    if (nextTokenIsParent)
      wtx.moveTo(parents.peekLong(0));
    else
      parents.push(key);
  }

  private long insertNullValue(final boolean nextTokenIsParent) {
    final long key;

    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertNullValueAsFirstChild().getNodeKey();
        } else {
          key = wtx.insertNullValueAsRightSibling().getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertNullValueAsLastChild().getNodeKey();
        } else {
          key = wtx.insertNullValueAsRightSibling().getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> key = wtx.insertNullValueAsLeftSibling().getNodeKey();
      case AS_RIGHT_SIBLING -> key = wtx.insertNullValueAsRightSibling().getNodeKey();
      default -> throw new AssertionError();//Should not happen
    }

    adaptTrxPosAndStack(nextTokenIsParent, key);

    return key;
  }

  private long insertArray() {
    long key = -1;
    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertArrayAsFirstChild().getNodeKey();
        } else {
          key = wtx.insertArrayAsRightSibling().getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertArrayAsLastChild().getNodeKey();
        } else {
          key = wtx.insertArrayAsRightSibling().getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> {
        if (wtx.getKind() == NodeKind.JSON_DOCUMENT
            || wtx.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
          throw new IllegalStateException(
              "Subtree can not be inserted as sibling of document root or the root-object/array/whatever!");
        }
        key = wtx.insertArrayAsLeftSibling().getNodeKey();
        insert = InsertPosition.AS_FIRST_CHILD;
      }
      case AS_RIGHT_SIBLING -> {
        if (wtx.getKind() == NodeKind.JSON_DOCUMENT
            || wtx.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
          throw new IllegalStateException(
              "Subtree can not be inserted as sibling of document root or the root-object/array/whatever!");
        }
        key = wtx.insertArrayAsRightSibling().getNodeKey();
        insert = InsertPosition.AS_FIRST_CHILD;
      }
      // $CASES-OMITTED$
      default -> throw new AssertionError();// Must not happen.
    }

    parents.popLong();
    parents.push(key);
    parents.push(Fixed.NULL_NODE_KEY.getStandardProperty());

    return key;
  }

  private long addObject() {
    long key = -1;
    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertObjectAsFirstChild().getNodeKey();
        } else {
          key = wtx.insertObjectAsRightSibling().getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertObjectAsLastChild().getNodeKey();
        } else {
          key = wtx.insertObjectAsRightSibling().getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> {
        if (wtx.getKind() == NodeKind.JSON_DOCUMENT
            || wtx.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
          throw new IllegalStateException(
              "Subtree can not be inserted as sibling of document root or the root-object/array/whatever!");
        }
        key = wtx.insertObjectAsLeftSibling().getNodeKey();
        insert = InsertPosition.AS_FIRST_CHILD;
      }
      case AS_RIGHT_SIBLING -> {
        if (wtx.getKind() == NodeKind.JSON_DOCUMENT
            || wtx.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
          throw new IllegalStateException(
              "Subtree can not be inserted as sibling of document root or the root-object/array/whatever!");
        }
        key = wtx.insertObjectAsRightSibling().getNodeKey();
        insert = InsertPosition.AS_FIRST_CHILD;
      }
      // $CASES-OMITTED$
      default -> throw new AssertionError();// Must not happen.
    }

    parents.popLong();
    parents.push(key);
    parents.push(Fixed.NULL_NODE_KEY.getStandardProperty());

    return key;
  }

  private void addObjectRecord(final String name, final ObjectRecordValue value,
      final boolean isNextTokenParentToken) {
    assert name != null;

    final long key;

    switch (insert) {
      case AS_FIRST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertObjectRecordAsFirstChild(name, value).getNodeKey();
        } else {
          key = wtx.insertObjectRecordAsRightSibling(name, value).getNodeKey();
        }
      }
      case AS_LAST_CHILD -> {
        if (parents.peekLong(0) == Fixed.NULL_NODE_KEY.getStandardProperty()) {
          key = wtx.insertObjectRecordAsLastChild(name, value).getNodeKey();
        } else {
          key = wtx.insertObjectRecordAsRightSibling(name, value).getNodeKey();
        }
      }
      case AS_LEFT_SIBLING -> key = wtx.insertObjectRecordAsLeftSibling(name, value).getNodeKey();
      case AS_RIGHT_SIBLING -> key = wtx.insertObjectRecordAsRightSibling(name, value).getNodeKey();
      default -> throw new AssertionError();//Should not happen
    }

    parents.popLong();
    parents.push(wtx.getParentKey());
    parents.push(Fixed.NULL_NODE_KEY.getStandardProperty());

    if (wtx.getKind() == NodeKind.OBJECT || wtx.getKind() == NodeKind.ARRAY) {
      parents.popLong();
      parents.push(key);
      parents.push(Fixed.NULL_NODE_KEY.getStandardProperty());
    } else {
      adaptTrxPosAndStack(isNextTokenParentToken, key);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy