blob: 69f8dfffbd751e13ffc90678dd844c7cf7c3b734 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.qpid.protonj2.engine.util;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.apache.qpid.protonj2.types.UnsignedInteger;
/**
* @param <E> The type stored in the map entries
*/
public class LinkedSplayMap<E> extends SplayMap<E> {
/**
* A dummy entry in the circular linked list of entries in the map.
* The first real entry is root.next, and the last is header.previous.
* If the map is empty, root.next == root && root.previous == root.
*/
private final transient SplayedEntry<E> entries = new SplayedEntry<>();
@Override
public void clear() {
super.clear();
// Unlink all the entries and reset to no insertions state
entries.linkNext = entries.linkPrev = entries;
}
@Override
public Collection<E> values() {
if (values == null) {
values = new LinkedSplayMapValues();
}
return values;
}
@Override
public Set<Entry<UnsignedInteger, E>> entrySet() {
if (entrySet == null) {
entrySet = new LinkedSplayMapEntrySet();
}
return entrySet;
}
@Override
public NavigableSet<UnsignedInteger> navigableKeySet() {
if (keySet == null) {
keySet = new LinkedSplayMapKeySet();
}
return keySet;
}
@Override
public void forEach(BiConsumer<? super UnsignedInteger, ? super E> action) {
Objects.requireNonNull(action);
SplayedEntry<E> root = this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != root; entry = entry.linkNext) {
action.accept(entry.getKey(), entry.getValue());
}
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
SplayedEntry<E> root = this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != root; entry = entry.linkNext) {
action.accept(entry.getValue());
}
}
@Override
public void replaceAll(BiFunction<? super UnsignedInteger, ? super E, ? extends E> function) {
Objects.requireNonNull(function, "The replacement function parameter cannot be null");
final int initialModCount = modCount;
for (SplayedEntry<E> e = entries.linkNext; e != entries; e = e.linkNext) {
e.value = function.apply(e.getKey(), e.value);
}
if (modCount != initialModCount) {
throw new ConcurrentModificationException();
}
}
@Override
protected void entryAdded(SplayedEntry<E> newEntry) {
// Insertion ordering of the splayed map entries recorded here
// and the list of entries doesn't change until an entry is removed.
newEntry.linkNext = entries;
newEntry.linkPrev = entries.linkPrev;
entries.linkPrev.linkNext = newEntry;
entries.linkPrev = newEntry;
}
@Override
protected void entryDeleted(SplayedEntry<E> deletedEntry) {
// Remove the entry from the insertion ordered entry list.
deletedEntry.linkNext.linkPrev = deletedEntry.linkPrev;
deletedEntry.linkPrev.linkNext = deletedEntry.linkNext;
deletedEntry.linkNext = deletedEntry.linkPrev = null;
}
//----- Map Iterator implementation for EntrySet, KeySet and Values collections
// Base class iterator that can be used for the collections returned from the Map
private abstract class LinkedSplayMapIterator<T> implements Iterator<T> {
private SplayedEntry<E> nextNode;
private SplayedEntry<E> lastReturned;
private int expectedModCount;
public LinkedSplayMapIterator(SplayedEntry<E> startAt) {
this.nextNode = startAt;
this.expectedModCount = LinkedSplayMap.this.modCount;
}
@Override
public boolean hasNext() {
return nextNode != entries;
}
protected SplayedEntry<E> nextNode() {
final SplayedEntry<E> entry = nextNode;
if (nextNode == entries) {
throw new NoSuchElementException();
}
if (expectedModCount != LinkedSplayMap.this.modCount) {
throw new ConcurrentModificationException();
}
nextNode = entry.linkNext;
return lastReturned = entry;
}
// Unused as of now but can be used for NavigableMap amongst other things
@SuppressWarnings("unused")
protected SplayedEntry<E> previousNode() {
final SplayedEntry<E> entry = nextNode;
if (nextNode == entries) {
throw new NoSuchElementException();
}
if (expectedModCount != LinkedSplayMap.this.modCount) {
throw new ConcurrentModificationException();
}
nextNode = nextNode.linkPrev;
return lastReturned = entry;
}
@Override
public void remove() {
if (lastReturned == null) {
throw new IllegalStateException();
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
delete(lastReturned);
expectedModCount = modCount;
lastReturned = null;
}
}
private class LinkedSplayMapEntryIterator extends LinkedSplayMapIterator<Entry<UnsignedInteger, E>> {
public LinkedSplayMapEntryIterator(SplayedEntry<E> startAt) {
super(startAt);
}
@Override
public Entry<UnsignedInteger, E> next() {
return nextNode();
}
}
private class LinkedSplayMapKeyIterator extends LinkedSplayMapIterator<UnsignedInteger> {
public LinkedSplayMapKeyIterator(SplayedEntry<E> startAt) {
super(startAt);
}
@Override
public UnsignedInteger next() {
return nextNode().getKey();
}
}
private class LinkedSplayMapValueIterator extends LinkedSplayMapIterator<E> {
public LinkedSplayMapValueIterator(SplayedEntry<E> startAt) {
super(startAt);
}
@Override
public E next() {
return nextNode().getValue();
}
}
//----- Splay Map Collection types
private final class LinkedSplayMapValues extends AbstractCollection<E> {
@Override
public Iterator<E> iterator() {
return new LinkedSplayMapValueIterator(entries.linkNext);
}
@Override
public int size() {
return LinkedSplayMap.this.size;
}
@Override
public boolean contains(Object o) {
return LinkedSplayMap.this.containsValue(o);
}
@Override
public boolean remove(Object target) {
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != root; entry = entry.linkNext) {
if (entry.valueEquals(target)) {
delete(entry);
return true;
}
}
return false;
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action, "forEach action parameter cannot be null");
final int initialModCount = modCount;
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != entries; entry = entry.linkNext) {
action.accept(entry.value);
}
if (modCount != initialModCount) {
throw new ConcurrentModificationException();
}
}
@Override
public void clear() {
LinkedSplayMap.this.clear();
}
}
private final class LinkedSplayMapKeySet extends SplayMapKeySet {
@Override
public Iterator<UnsignedInteger> iterator() {
return new LinkedSplayMapKeyIterator(entries.linkNext);
}
@Override
public boolean remove(Object target) {
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != root; entry = entry.linkNext) {
if (entry.keyEquals(target)) {
delete(entry);
return true;
}
}
return false;
}
@Override
public void forEach(Consumer<? super UnsignedInteger> action) {
Objects.requireNonNull(action, "forEach action parameter cannot be null");
final int initialModCount = modCount;
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != entries; entry = entry.linkNext) {
action.accept(entry.getKey());
}
if (modCount != initialModCount) {
throw new ConcurrentModificationException();
}
}
}
private final class LinkedSplayMapEntrySet extends AbstractSet<Entry<UnsignedInteger, E>> {
@Override
public Iterator<Entry<UnsignedInteger, E>> iterator() {
return new LinkedSplayMapEntryIterator(entries.linkNext);
}
@Override
public int size() {
return LinkedSplayMap.this.size;
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != root; entry = entry.linkNext) {
if (entry.equals(o)) {
return true;
}
}
return false;
}
@Override
public void forEach(Consumer<? super Entry<UnsignedInteger, E>> action) {
Objects.requireNonNull(action, "forEach action parameter cannot be null");
final int initialModCount = modCount;
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != entries; entry = entry.linkNext) {
action.accept(entry);
}
if (modCount != initialModCount) {
throw new ConcurrentModificationException();
}
}
@Override
public boolean remove(Object target) {
if (!(target instanceof Entry)) {
throw new IllegalArgumentException("value provided is not an Entry type.");
}
final SplayedEntry<E> root = LinkedSplayMap.this.entries;
for (SplayedEntry<E> entry = root.linkNext; entry != root; entry = entry.linkNext) {
if (entry.equals(target)) {
delete(entry);
return true;
}
}
return false;
}
@Override
public void clear() {
LinkedSplayMap.this.clear();
}
}
}