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

org.projectnessie.versioned.mongodb.MongoRef Maven / Gradle / Ivy

There is a newer version: 0.9.2
Show newest version
/*
 * Copyright (C) 2020 Dremio
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.projectnessie.versioned.mongodb;

import static org.projectnessie.versioned.mongodb.MongoSerDe.deserializeId;
import static org.projectnessie.versioned.mongodb.MongoSerDe.deserializeIds;
import static org.projectnessie.versioned.mongodb.MongoSerDe.deserializeKeyMutations;

import com.google.common.primitives.Ints;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.bson.BsonWriter;
import org.bson.Document;
import org.projectnessie.versioned.store.Id;
import org.projectnessie.versioned.tiered.Mutation;
import org.projectnessie.versioned.tiered.Ref;

final class MongoRef extends MongoBaseValue implements Ref {

  static final String TYPE = "type";
  static final String NAME = "name";
  static final String COMMIT = "commit";
  static final String COMMITS = "commits";
  static final String DELTAS = "deltas";
  static final String PARENT = "parent";
  static final String POSITION = "position";
  static final String NEW_ID = "new";
  static final String OLD_ID = "old";
  static final String REF_TYPE_BRANCH = "b";
  static final String REF_TYPE_TAG = "t";
  static final String TREE = "tree";
  static final String METADATA = "metadata";
  static final String KEY_LIST = "keys";

  static void produce(Document document, Ref v) {
    v = produceBase(document, v).name(document.getString(NAME));
    String type = document.getString(TYPE);
    switch (type) {
      case REF_TYPE_TAG:
        v.tag().commit(deserializeId(document, COMMIT));
        break;
      case REF_TYPE_BRANCH:
        v.branch()
            .metadata(deserializeId(document, METADATA))
            .children(deserializeIds(document, TREE))
            .commits(bc -> deserializeBranchCommits(bc, document));
        break;
      default:
        throw new IllegalArgumentException("Unknown ref-type " + type);
    }
  }

  private enum Type {
    INIT,
    TAG,
    BRANCH
  }

  private Type type = Type.INIT;

  MongoRef(BsonWriter bsonWriter) {
    super(bsonWriter);
  }

  @Override
  public Tag tag() {
    if (type != Type.INIT) {
      throw new IllegalStateException("branch()/tag() has already been called");
    }
    type = Type.TAG;
    serializeString(TYPE, REF_TYPE_TAG);
    return new MongoTag();
  }

  @Override
  public Branch branch() {
    if (type != Type.INIT) {
      throw new IllegalStateException("branch()/tag() has already been called");
    }
    type = Type.BRANCH;
    serializeString(TYPE, REF_TYPE_BRANCH);
    return new MongoBranch();
  }

  @Override
  public Ref name(String name) {
    serializeString(NAME, name);
    return this;
  }

  class MongoTag implements Tag {
    @Override
    public Tag commit(Id commit) {
      serializeId(COMMIT, commit);
      return this;
    }

    @Override
    public Ref backToRef() {
      return MongoRef.this;
    }
  }

  class MongoBranch implements Branch {
    @Override
    public Branch metadata(Id metadata) {
      serializeId(METADATA, metadata);
      return this;
    }

    @Override
    public Branch children(Stream children) {
      serializeIds(TREE, children);
      return this;
    }

    @Override
    public Branch commits(Consumer commits) {
      addProperty(COMMITS);
      bsonWriter.writeStartArray(COMMITS);
      commits.accept(new MongoBranchCommit());
      bsonWriter.writeEndArray();
      return this;
    }

    @Override
    public Ref backToRef() {
      return MongoRef.this;
    }
  }

  private class MongoBranchCommit
      implements BranchCommit, SavedCommit, UnsavedCommitDelta, UnsavedCommitMutations {

    int state;

    @Override
    public BranchCommit id(Id id) {
      maybeStart();
      assertState(1);
      MongoSerDe.serializeId(bsonWriter, ID, id);
      return this;
    }

    @Override
    public BranchCommit commit(Id commit) {
      maybeStart();
      assertState(1);
      MongoSerDe.serializeId(bsonWriter, COMMIT, commit);
      return this;
    }

    @Override
    public SavedCommit saved() {
      return this;
    }

    @Override
    public UnsavedCommitDelta unsaved() {
      return this;
    }

    @Override
    public SavedCommit parent(Id parent) {
      maybeStart();
      assertState(1);
      MongoSerDe.serializeId(bsonWriter, PARENT, parent);
      return this;
    }

    @Override
    public UnsavedCommitDelta delta(int position, Id oldId, Id newId) {
      maybeStart();
      if (state == 1) {
        state = 2;
        bsonWriter.writeStartArray(DELTAS);
      }
      assertState(2);
      serializeDelta(bsonWriter, position, oldId, newId);
      return this;
    }

    @Override
    public UnsavedCommitMutations mutations() {
      return this;
    }

    @Override
    public UnsavedCommitMutations keyMutation(Mutation keyMutation) {
      maybeStart();
      if (state == 2) {
        state = 1;
        bsonWriter.writeEndArray();
      }
      if (state == 1) {
        state = 3;
        bsonWriter.writeStartArray(KEY_LIST);
      }
      assertState(3);
      MongoSerDe.serializeKeyMutation(bsonWriter, keyMutation);
      return this;
    }

    private void maybeStart() {
      if (state == 0) {
        bsonWriter.writeStartDocument();
        state = 1;
      }
    }

    private void assertState(int expected) {
      if (state != expected) {
        throw new IllegalStateException(
            "Wrong order or consumer method invocations ("
                + expected
                + " != "
                + state
                + ". See Javadocs.");
      }
    }

    @Override
    public BranchCommit done() {
      if (state == 3 || state == 2) {
        bsonWriter.writeEndArray();
        state = 1;
      }
      if (state == 1) {
        bsonWriter.writeEndDocument();
        state = 0;
      }
      return this;
    }
  }

  @Override
  BsonWriter build() {
    checkPresent(NAME, "name");
    checkPresent(TYPE, "type");

    switch (type) {
      case TAG:
        checkPresent(COMMIT, "commit");
        checkNotPresent(COMMITS, "commits");
        checkNotPresent(TREE, "tree");
        checkNotPresent(METADATA, "metadata");
        break;
      case BRANCH:
        checkNotPresent(COMMIT, "commit");
        checkPresent(COMMITS, "commits");
        checkPresent(TREE, "tree");
        checkPresent(METADATA, "metadata");
        break;
      default:
        throw new IllegalStateException("Neither tag() nor branch() has been called");
    }

    return super.build();
  }

  private void serializeDelta(BsonWriter writer, int position, Id oldId, Id newId) {
    writer.writeStartDocument();
    serializeLong(POSITION, position);
    MongoSerDe.serializeId(writer, OLD_ID, oldId);
    MongoSerDe.serializeId(writer, NEW_ID, newId);
    writer.writeEndDocument();
  }

  static void deserializeBranchCommits(BranchCommit bc, Document d) {
    @SuppressWarnings("unchecked")
    List lst = (List) d.get(COMMITS);
    lst.forEach(c -> deserializeBranchCommit(bc, c));
  }

  private static void deserializeBranchCommit(BranchCommit consumer, Document d) {
    consumer = consumer.id(deserializeId(d, ID)).commit(deserializeId(d, COMMIT));
    if (d.containsKey(PARENT)) {
      consumer.saved().parent(deserializeId(d, PARENT)).done();
    } else {
      UnsavedCommitDelta unsaved = consumer.unsaved();
      @SuppressWarnings("unchecked")
      List deltas = (List) d.get(DELTAS);
      deltas.forEach(delta -> deserializeUnsavedDelta(unsaved, delta));
      UnsavedCommitMutations mutations = unsaved.mutations();
      deserializeKeyMutations(d, KEY_LIST).forEach(mutations::keyMutation);
      mutations.done();
    }
  }

  private static void deserializeUnsavedDelta(UnsavedCommitDelta consumer, Document d) {
    int position = Ints.saturatedCast(d.getLong(POSITION));
    Id oldId = deserializeId(d, OLD_ID);
    Id newId = deserializeId(d, NEW_ID);
    consumer.delta(position, oldId, newId);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy