/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.internal.regions; import com.jcwhatever.nucleus.regions.IRegion; import com.jcwhatever.nucleus.regions.options.RegionEventPriority; import com.jcwhatever.nucleus.regions.options.RegionEventPriority.PriorityType; import com.jcwhatever.nucleus.utils.PreCon; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Ordering collection container for regions. * * <p>Allows retrieving regions sorted by priority while maintaining * {@link java.util.HashSet#contains} method performance.</p> * * <p>If only the {@link java.util.Set} implemented methods are used, the collection * performance should remain nearly the same as a {@link java.util.HashSet}.</p> */ class EventOrderedRegions<E extends IRegion> implements Set<E> { // primary hash set private final Set<E> _hashSet; // enter event sorted list cache private List<E> _enterList; private boolean _isEnterSorted; // leave event sorted list cache private List<E> _leaveList; private boolean _isLeaveSorted; /** * Constructor. */ public EventOrderedRegions() { this(10); } /** * Constructor. * * @param size The initial capacity. */ public EventOrderedRegions(int size) { _hashSet = new HashSet<>(size); } @Override public int size() { return _hashSet.size(); } @Override public boolean isEmpty() { return _hashSet.isEmpty(); } @Override public boolean contains(Object obj) { //noinspection SuspiciousMethodCalls return _hashSet.contains(obj); } @Override public Iterator<E> iterator() { return _hashSet.iterator(); } /** * Get an iterator whose elements are sorted by the * specified priority. * * @param priorityType The sorting priority type. */ public Iterator<E> iterator(final PriorityType priorityType) { PreCon.notNull(priorityType); sort(priorityType); return new Iterator<E>() { private Iterator<E> _iterator = getList(priorityType).iterator(); private E _current; @Override public boolean hasNext() { return _iterator.hasNext(); } @Override public E next() { return _current = _iterator.next(); } @Override public void remove() { _iterator.remove(); switch (priorityType) { // enter list removed case ENTER: // now remove from leave list if (_leaveList != null) { _leaveList.remove(_current); } break; // leave list removed case LEAVE: // now remove from enter list if (_enterList != null) { _enterList.remove(_current); } break; default: throw new AssertionError(); } _hashSet.remove(_current); } }; } @Override public Object[] toArray() { return _hashSet.toArray(); } /** * Get an object array from the collection whose * elements are sorted by the specified priority type. * * @param priorityType The priority type. */ public Object[] toArray(PriorityType priorityType) { PreCon.notNull(priorityType); sort(priorityType); return getList(priorityType).toArray(); } @Override public <T> T[] toArray(T[] array) { //noinspection SuspiciousToArrayCall return _hashSet.toArray(array); } /** * Get an array from the collection whose elements * are sorted by the specified priority type. * * @param priorityType The priority type. * @param array The array to place the elements into. * * @param <T> The array type. */ public <T> T[] toArray(PriorityType priorityType, T[] array) { PreCon.notNull(priorityType); PreCon.notNull(array); sort(priorityType); //noinspection SuspiciousToArrayCall return getList(priorityType).toArray(array); } @Override public boolean add(E entry) { PreCon.notNull(entry); if (_hashSet.add(entry)) { if (_enterList != null) { _enterList.add(entry); } if (_leaveList != null) { _leaveList.add(entry); } _isEnterSorted = false; _isLeaveSorted = false; return true; } return false; } @Override public boolean remove(Object obj) { PreCon.notNull(obj); //noinspection SuspiciousMethodCalls if (_hashSet.remove(obj)) { if (_enterList != null) { //noinspection SuspiciousMethodCalls _enterList.remove(obj); } if (_leaveList != null) { //noinspection SuspiciousMethodCalls _leaveList.remove(obj); } return true; } return false; } @Override public boolean containsAll(Collection<?> collection) { PreCon.notNull(collection); return _hashSet.containsAll(collection); } @Override public boolean addAll(Collection<? extends E> collection) { PreCon.notNull(collection); boolean isModified = false; for (E entry : collection) { isModified = add(entry) || isModified; } return isModified; } @Override public boolean retainAll(Collection<?> collection) { PreCon.notNull(collection); if (_hashSet.retainAll(collection)) { if (_enterList != null) { _enterList.retainAll(collection); } if (_leaveList != null) { _leaveList.retainAll(collection); } return true; } return false; } @Override public boolean removeAll(Collection<?> collection) { PreCon.notNull(collection); boolean isModified = false; for (Object entry : collection) { isModified = remove(entry) || isModified; } return isModified; } @Override public void clear() { _hashSet.clear(); if (_enterList != null) _enterList.clear(); if (_leaveList != null) _leaveList.clear(); } /** * Sort the collection. * * <p>Will not sort if the collection does not need to be sorted.</p> */ public void sort(final PriorityType priorityType) { PreCon.notNull(priorityType); switch (priorityType) { case ENTER: if (_isEnterSorted) return; _isEnterSorted = true; break; case LEAVE: if (_isLeaveSorted) return; _isLeaveSorted = true; break; default: throw new AssertionError(); } List<E> list = getList(priorityType); Collections.sort(list, new Comparator<E>() { @Override public int compare(E o1, E o2) { RegionEventPriority p1 = o1.getEventPriority(priorityType); RegionEventPriority p2 = o2.getEventPriority(priorityType); return Integer.compare(p1.getSortOrder(), p2.getSortOrder()); } }); } private List<E> getList(PriorityType priorityType) { switch (priorityType) { case ENTER: if (_enterList == null) { _enterList = new ArrayList<>(_hashSet); } return _enterList; case LEAVE: if (_leaveList == null) { _leaveList = new ArrayList<>(_hashSet); } return _leaveList; default: throw new AssertionError(); } } }