io.sirix.service.json.shredder.JsonItemShredder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sirix-core Show documentation
Show all versions of sirix-core Show documentation
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.
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