| /* |
| * 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(); |
| } |
| } |
| } |