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

com.vaadin.collaborationengine.EntryList Maven / Gradle / Ivy

/*
 * Copyright 2020-2022 Vaadin Ltd.
 *
 * This program is available under Commercial Vaadin Runtime License 1.0
 * (CVRLv1).
 *
 * For the full License, see http://vaadin.com/license/cvrl-1
 */
package com.vaadin.collaborationengine;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Stream;
import java.util.stream.Stream.Builder;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;

/**
 * A linked list where each entry can be looked up using a generated key.
 *
 * @author Vaadin Ltd
 */
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
class EntryList {
    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
    private static class ListEntry {
        JsonNode value;
        UUID prev;
        UUID next;
        UUID revisionId;
        UUID scopeOwnerId;
    }

    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
    static class ListEntrySnapshot {
        final UUID id;
        final JsonNode value;
        final UUID prev;
        final UUID next;
        final UUID revisionId;
        final UUID scopeOwnerId;

        ListEntrySnapshot(UUID id, ListEntry entry) {
            this.id = id;
            this.value = entry.value;
            this.prev = entry.prev;
            this.next = entry.next;
            this.revisionId = entry.revisionId;
            this.scopeOwnerId = entry.scopeOwnerId;
        }

        @JsonCreator
        ListEntrySnapshot(@JsonProperty("id") UUID id,
                @JsonProperty("value") JsonNode value,
                @JsonProperty("prev") UUID prev,
                @JsonProperty("next") UUID next,
                @JsonProperty("revisionId") UUID revisionId,
                @JsonProperty("scopeOwnerId") UUID scopeOwnerId) {
            this.id = id;
            this.value = value;
            this.prev = prev;
            this.next = next;
            this.revisionId = revisionId;
            this.scopeOwnerId = scopeOwnerId;
        }
    }

    private final Map entries = new HashMap<>();
    private UUID head;
    private UUID tail;

    int size() {
        return entries.size();
    }

    void clear() {
        entries.clear();
        head = null;
        tail = null;
    }

    ListEntrySnapshot insertFirst(UUID key, JsonNode value, UUID revisionId,
            UUID scopeOwnerId) {
        ListEntry item = createAndAddItem(key, value, revisionId, scopeOwnerId);

        link(key, null, head);

        return new ListEntrySnapshot(key, item);
    }

    ListEntrySnapshot insertLast(UUID key, JsonNode value, UUID revisionId,
            UUID scopeOwnerId) {
        ListEntry item = createAndAddItem(key, value, revisionId, scopeOwnerId);

        link(key, tail, null);

        return new ListEntrySnapshot(key, item);
    }

    ListEntrySnapshot insertBefore(UUID keyToFind, UUID keyToInsert,
            JsonNode value, UUID revisionId, UUID scopeOwnerId) {
        ListEntry item = createAndAddItem(keyToInsert, value, revisionId,
                scopeOwnerId);

        ListEntry entryToFind = entries.get(keyToFind);
        link(keyToInsert, entryToFind.prev, keyToFind);

        return new ListEntrySnapshot(keyToInsert, item);
    }

    ListEntrySnapshot insertAfter(UUID keyToFind, UUID keyToInsert,
            JsonNode value, UUID revisionId, UUID scopeOwnerId) {
        ListEntry item = createAndAddItem(keyToInsert, value, revisionId,
                scopeOwnerId);

        ListEntry entryToFind = entries.get(keyToFind);
        link(keyToInsert, keyToFind, entryToFind.next);

        return new ListEntrySnapshot(keyToInsert, item);
    }

    ListEntrySnapshot moveBefore(UUID keyToFind, UUID keyToMove,
            UUID revisionId, UUID scopeOwnerId) {
        ListEntry entryToMove = entries.get(keyToMove);
        if (entryToMove != null) {
            entryToMove.revisionId = revisionId;
            if (Objects.equals(scopeOwnerId, JsonUtil.TOPIC_SCOPE_ID)) {
                entryToMove.scopeOwnerId = null;
            } else if (scopeOwnerId != null) {
                entryToMove.scopeOwnerId = scopeOwnerId;
            }
        }

        unlink(entryToMove);

        ListEntry entryToFind = entries.get(keyToFind);
        link(keyToMove, entryToFind.prev, keyToFind);

        return new ListEntrySnapshot(keyToMove, entryToMove);
    }

    ListEntrySnapshot moveAfter(UUID keyToFind, UUID keyToMove, UUID revisionId,
            UUID scopeOwnerId) {
        ListEntry entryToMove = entries.get(keyToMove);
        if (entryToMove != null) {
            entryToMove.revisionId = revisionId;
            if (Objects.equals(scopeOwnerId, JsonUtil.TOPIC_SCOPE_ID)) {
                entryToMove.scopeOwnerId = null;
            } else if (scopeOwnerId != null) {
                entryToMove.scopeOwnerId = scopeOwnerId;
            }
        }

        unlink(entryToMove);

        ListEntry entryToFind = entries.get(keyToFind);
        link(keyToMove, keyToFind, entryToFind.next);

        return new ListEntrySnapshot(keyToMove, entryToMove);
    }

    Stream stream() {
        Builder builder = Stream.builder();

        UUID key = head;
        while (key != null) {
            ListEntry entry = entries.get(key);
            builder.add(new ListEntrySnapshot(key, entry));
            key = entry.next;
        }

        return builder.build();
    }

    JsonNode getValue(UUID key) {
        ListEntry item = entries.get(key);

        if (item == null) {
            return null;
        }

        return item.value;
    }

    ListEntrySnapshot getEntry(UUID key) {
        ListEntry entry = entries.get(key);
        if (entry == null) {
            return null;
        } else {
            return new ListEntrySnapshot(key, entry);
        }
    }

    void remove(UUID key) {
        ListEntry item = entries.remove(key);
        unlink(item);
    }

    private void unlink(ListEntry item) {
        if (item != null) {
            setPrev(item.next, item.prev);
            setNext(item.prev, item.next);
        }
    }

    private void link(UUID key, UUID keyBefore, UUID keyAfter) {
        ListEntry item = entries.get(key);
        if (item != null) {
            item.prev = keyBefore;
            item.next = keyAfter;
            setPrev(item.next, key);
            setNext(item.prev, key);
        }
    }

    private void setNext(UUID target, UUID value) {
        if (target == null) {
            head = value;
        } else {
            entries.get(target).next = value;
        }
    }

    private void setPrev(UUID target, UUID value) {
        if (target == null) {
            tail = value;
        } else {
            entries.get(target).prev = value;
        }
    }

    private ListEntry createAndAddItem(UUID key, JsonNode value,
            UUID revisionId, UUID scopeOwnerId) {
        ListEntry item = new ListEntry();
        item.value = value;
        item.revisionId = revisionId;
        item.scopeOwnerId = scopeOwnerId;
        entries.put(Objects.requireNonNull(key), item);

        return item;
    }

    void setValue(UUID key, JsonNode newValue, UUID revisionId,
            UUID scopeOwnerId) {
        ListEntry listEntry = entries.get(key);
        listEntry.value = newValue;
        listEntry.revisionId = revisionId;
        listEntry.scopeOwnerId = scopeOwnerId;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy