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

com.vaadin.collaborationengine.ListOperation 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.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * A list operation providing details such as the operation type, related
 * properties and conditions that should be met for the operation to succeed.
 *
 * @author Vaadin Ltd
 */
public class ListOperation {
    public enum OperationType {
        INSERT_BEFORE, INSERT_AFTER, MOVE_BEFORE, MOVE_AFTER, SET
    }

    /**
     * Creates a list operation to insert the given value as the first item of
     * the list.
     *
     * @param value
     *            the value, not null
     * @return the list operation, not null
     */
    public static ListOperation insertFirst(Object value) {
        Objects.requireNonNull(value);
        return new ListOperation(OperationType.INSERT_AFTER, value, null, null);
    }

    /**
     * Creates a list operation to insert the given value as the last item of
     * the list.
     *
     * @param value
     *            the value, not null
     * @return the list operation, not null
     */
    public static ListOperation insertLast(Object value) {
        Objects.requireNonNull(value);
        return new ListOperation(OperationType.INSERT_BEFORE, value, null,
                null);
    }

    /**
     * Creates a list operation to insert the given value just before the
     * position specified by the given key.
     *
     * @param before
     *            the position key, not null
     * @param value
     *            the value, not null
     * @return the list operation, not null
     */
    public static ListOperation insertBefore(ListKey before, Object value) {
        Objects.requireNonNull(before);
        Objects.requireNonNull(value);
        return new ListOperation(OperationType.INSERT_BEFORE, value, null,
                before);
    }

    /**
     * Creates a list operation to insert the given value just after the
     * position specified by the given key.
     *
     * @param after
     *            the position key, not null
     * @param value
     *            the value, not null
     * @return the list operation, not null
     */
    public static ListOperation insertAfter(ListKey after, Object value) {
        Objects.requireNonNull(after);
        Objects.requireNonNull(value);
        return new ListOperation(OperationType.INSERT_AFTER, value, null,
                after);
    }

    /**
     * Creates a list operation to insert the given value between the positions
     * specified by the given keys. If the given keys are not adjacent, the
     * operation will fail.
     *
     * @param prev
     *            the position of the previous item, not null
     * @param next
     *            the position of the next item, not null
     * @param value
     *            the value, not null
     * @return the list operation, not null
     */
    public static ListOperation insertBetween(ListKey prev, ListKey next,
            Object value) {
        Objects.requireNonNull(prev);
        Objects.requireNonNull(next);
        Objects.requireNonNull(value);
        return insertAfter(prev, value).ifNext(prev, next);
    }

    /**
     * Creates a list operation to move the given entry to just before the
     * position specified by the given key.
     *
     * @param before
     *            the position key, not null
     * @param entry
     *            the entry key to move, not null
     * @return the list operation, not null
     */
    public static ListOperation moveBefore(ListKey before, ListKey entry) {
        Objects.requireNonNull(before);
        Objects.requireNonNull(entry);
        return new ListOperation(OperationType.MOVE_BEFORE, null, entry,
                before);
    }

    /**
     * Creates a list operation to move the given entry to just after the
     * position specified by the given key.
     *
     * @param after
     *            the position key, not null
     * @param entry
     *            the entry key to move, not null
     * @return the list operation, not null
     */
    public static ListOperation moveAfter(ListKey after, ListKey entry) {
        Objects.requireNonNull(after);
        Objects.requireNonNull(entry);
        return new ListOperation(OperationType.MOVE_AFTER, null, entry, after);
    }

    /**
     * Creates a list operation to move the given entry between the positions
     * specified by the given keys. If the given keys are not adjacent, the
     * operation will fail.
     *
     * @param prev
     *            the position of the previous item, not null
     * @param next
     *            the position of the next item, not null
     * @param entry
     *            the entry key to move, not null
     * @return the list operation, not null
     */
    public static ListOperation moveBetween(ListKey prev, ListKey next,
            ListKey entry) {
        Objects.requireNonNull(prev);
        Objects.requireNonNull(next);
        Objects.requireNonNull(entry);
        return moveAfter(prev, entry).ifNext(prev, next);
    }

    /**
     * Creates a list operation to set the given value at the position specified
     * by the given key. By default, the value will be set as topic scope.
     *
     * @param key
     *            the position key, not null
     * @param value
     *            the value
     * @return the list operation, not null
     */
    public static ListOperation set(ListKey key, Object value) {
        Objects.requireNonNull(key);
        return new ListOperation(OperationType.SET, value, key, null);
    }

    /**
     * Creates a list operation to delete the value at the position specified by
     * the given key.
     *
     * @param key
     *            the position key, not null
     * @return the list operation, not null
     */
    public static ListOperation delete(ListKey key) {
        return set(key, null);
    }

    private final Object value;

    private final OperationType type;

    private final ListKey changeKey;

    private final ListKey referenceKey;

    private final Map conditions = new HashMap<>();

    private EntryScope scope = EntryScope.TOPIC;

    private Boolean empty;

    private final Map valueConditions = new HashMap<>();

    private ListOperation(OperationType type, Object value, ListKey changeKey,
            ListKey referenceKey) {
        this.type = type;
        this.value = value;
        this.changeKey = changeKey;
        this.referenceKey = referenceKey;
    }

    /**
     * Sets the scope of the item affected by this operation. If not set, the
     * default scope will be {@link EntryScope#TOPIC}. Values with
     * {@link EntryScope#CONNECTION} scope will be automatically removed once
     * the connection to the topic which created them is deactivated.
     *
     * @param scope
     *            the scope, not null
     * @return this operation, not null
     */
    public ListOperation withScope(EntryScope scope) {
        this.scope = Objects.requireNonNull(scope);
        return this;
    }

    /**
     * Adds a condition that requires the specified nextKey to be
     * right after the specified key when the operation is applied.
     * A null nextKey can be used to represent the
     * tail of the list.
     *
     * @param key
     *            the reference key, not null
     * @param nextKey
     *            the required key, or null to represent the tail
     *            of the list
     * @return this operation, not null
     */
    public ListOperation ifNext(ListKey key, ListKey nextKey) {
        Objects.requireNonNull(key);
        if (conditions.containsKey(key)) {
            throw new IllegalStateException(
                    "A requirement for the value after this key is already set");
        }
        conditions.put(key, nextKey);
        return this;
    }

    /**
     * Adds a condition that requires the specified key to be the
     * last in the list.
     *
     * @param key
     *            the key, not null
     * @return this operation, not null
     */
    public ListOperation ifLast(ListKey key) {
        Objects.requireNonNull(key);
        return ifNext(key, null);
    }

    /**
     * Adds a condition that requires the specified prevKey to be
     * right before the specified key when the operation is
     * applied. A null prevKey can be used to
     * represent the head of the list.
     *
     * @param key
     *            the reference key, not null
     * @param prevKey
     *            the required key, or null to represent the head
     *            of the list
     * @return this operation, not null
     */
    public ListOperation ifPrev(ListKey key, ListKey prevKey) {
        Objects.requireNonNull(key);
        if (conditions.containsValue(key)) {
            throw new IllegalStateException(
                    "A requirement for the value before this key is already set");
        }
        conditions.put(prevKey, key);
        return this;
    }

    /**
     * Adds a condition that requires the specified key to be the
     * first in the list.
     *
     * @param key
     *            the key, not null
     * @return this operation, not null
     */
    public ListOperation ifFirst(ListKey key) {
        Objects.requireNonNull(key);
        return ifPrev(key, null);
    }

    /**
     * Adds a condition that requires the list to be empty.
     *
     * @return this operation, not null
     */
    public ListOperation ifEmpty() {
        if (Boolean.FALSE.equals(empty)) {
            throw new IllegalStateException(
                    "This operation already requires the list not to be empty.");
        }
        empty = true;
        return this;
    }

    /**
     * Adds a condition that requires the list not to be empty.
     *
     * @return this operation, not null
     */
    public ListOperation ifNotEmpty() {
        if (Boolean.TRUE.equals(empty)) {
            throw new IllegalStateException(
                    "This operation already requires the list to be empty.");
        }
        empty = false;
        return this;
    }

    /**
     * Add a condition that requires the specified key to have the
     * specified value.
     *
     * @param key
     *            the key, not null
     * @param value
     *            the expected value
     * @return this operation, not null
     */
    public ListOperation ifValue(ListKey key, Object value) {
        Objects.requireNonNull(key);
        if (valueConditions.containsKey(key)) {
            throw new IllegalStateException(
                    "A requirement for the value of this key is already set");
        }
        valueConditions.put(key, value);
        return this;
    }

    Object getValue() {
        return value;
    }

    OperationType getType() {
        return type;
    }

    ListKey getReferenceKey() {
        return referenceKey;
    }

    ListKey getChangeKey() {
        return changeKey;
    }

    EntryScope getScope() {
        return scope;
    }

    Map getConditions() {
        return Collections.unmodifiableMap(conditions);
    }

    Boolean getEmpty() {
        return empty;
    }

    Map getValueConditions() {
        return Collections.unmodifiableMap(valueConditions);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy