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

org.fxmisc.undo.impl.FixedSizeChangeQueue Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
package org.fxmisc.undo.impl;

import java.util.NoSuchElementException;

public class FixedSizeChangeQueue implements ChangeQueue {

    private class QueuePositionImpl implements QueuePosition {
        private final int arrayPos;
        private final long rev;

        QueuePositionImpl(int arrayPos, long rev) {
            this.arrayPos = arrayPos;
            this.rev = rev;
        }

        @Override
        public boolean isValid() {
            int pos = relativize(arrayPos);
            if(pos <= size) {
                return rev == fetchRevisionForPosition(pos)
                        // if the queue is full, then position 0 can also mean the last position ( = capacity)
                        || pos == 0 && size == capacity && rev == fetchRevisionForPosition(capacity);
            } else {
                return false;
            }
        }

        @Override
        public boolean equals(Object other) {
            if(other instanceof FixedSizeChangeQueue.QueuePositionImpl) {
                @SuppressWarnings("unchecked")
                QueuePositionImpl otherPos = (QueuePositionImpl) other;
                return getQueue() == otherPos.getQueue() && rev == otherPos.rev;
            } else {
                return false;
            }
        }

        private FixedSizeChangeQueue getQueue() {
            return FixedSizeChangeQueue.this;
        }
    }

    private final RevisionedChange[] changes;
    private final int capacity;
    private int start = 0;
    private int size = 0;

    // current position is always from the interval [0, size],
    // i.e. not offset by start
    private int currentPosition = 0;

    private long revision = 0;
    private long zeroPositionRevision = revision;

    @SuppressWarnings("unchecked")
    public FixedSizeChangeQueue(int capacity) {
        if(capacity <= 0) {
            throw new IllegalArgumentException("capacity must be positive");
        }

        this.capacity = capacity;
        this.changes = new RevisionedChange[capacity];
    }

    @Override
    public boolean hasNext() {
        return currentPosition < size;
    }

    @Override
    public boolean hasPrev() {
        return currentPosition > 0;
    }

    @Override
    public C peekNext() {
        if(currentPosition < size) {
            return fetch(currentPosition).getChange();
        } else {
            throw new NoSuchElementException();
        }
    }

    @Override
    public C next() {
        C c = peekNext();
        currentPosition += 1;
        return c;
    }

    @Override
    public C peekPrev() {
        if(currentPosition > 0) {
            return fetch(currentPosition - 1).getChange();
        } else {
            throw new NoSuchElementException();
        }
    }

    @Override
    public C prev() {
        C c = peekPrev();
        currentPosition -= 1;
        return c;
    }

    @Override
    public void forgetHistory() {
        zeroPositionRevision = fetchRevisionForPosition(currentPosition);
        start = arrayIndex(currentPosition);
        size -= currentPosition;
        currentPosition = 0;
    }

    @Override
    @SafeVarargs
    public final void push(C... changes) {
        RevisionedChange lastOverwrittenChange = null;
        for(C c: changes) {
            RevisionedChange revC = new RevisionedChange<>(c, ++revision);
            lastOverwrittenChange = put(currentPosition++, revC);
        }

        if(currentPosition > capacity) {
            start = arrayIndex(currentPosition);
            currentPosition = capacity;
            size = capacity;
            zeroPositionRevision = lastOverwrittenChange.getRevision();
        } else {
            size = currentPosition;
        }
    }

    @Override
    public QueuePosition getCurrentPosition() {
        long rev = fetchRevisionForPosition(currentPosition);
        return new QueuePositionImpl(arrayIndex(currentPosition), rev);
    }

    private long fetchRevisionForPosition(int position) {
        if(position == 0) {
            return zeroPositionRevision;
        } else {
            return fetch(position - 1).getRevision();
        }
    }

    private RevisionedChange fetch(int position) {
        return changes[arrayIndex(position)];
    }

    private RevisionedChange put(int position, RevisionedChange c) {
        RevisionedChange old = changes[arrayIndex(position)];
        changes[arrayIndex(position)] = c;
        return old;
    }

    // returns a number from [0..capacity-1]
    private int arrayIndex(int queuePosition) {
        return (start + queuePosition) % capacity;
    }

    // inverse of arrayPos
    private int relativize(int arrayPos) {
        return (arrayPos - start + capacity) % capacity;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy