/**
* Copyright (c) 2005-2017, KoLmafia development team
* http://kolmafia.sourceforge.net/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* [1] Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* [2] Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* [3] Neither the name "KoLmafia" nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.kolmafia.utilities;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.ListIterator;
/**
* Same as {@link net.java.dev.spellcast.utilities.SortedListModel}, except this extends an ArrayList
*/
public class SortedList<E>
extends ArrayList<E>
{
/**
* Please refer to {@link java.util.List#add(int,Object)} for more information regarding this function. Note that if
* the position is invalid (ie: it does not result in a sorted property), the element will be successfully added,
* but to a different position.
*/
@Override
public void add( final int index, final E element )
{
if ( element == null )
{
return;
}
this.add( element );
}
/**
* Please refer to {@link java.util.List#add(Object)} for more information regarding this function.
*/
@Override
public boolean add( final E o )
{
if ( o == null )
{
return false;
}
try
{
super.add( this.insertionIndexOf( 0, this.size() - 1, (Comparable) o ), o );
return true;
}
catch ( IllegalArgumentException e1 )
{
return false;
}
catch ( ClassCastException e2 )
{
return false;
}
}
/**
* Please refer to {@link java.util.List#addAll(int,Collection)} for more information regarding this function.
*/
public boolean addAll( final Collection<? extends E> c )
{
return addAll( size(), c );
}
@Override
public boolean addAll( final int index, final Collection<? extends E> c )
{
synchronized ( this )
{
if ( !super.addAll( index, c ) )
{
return false;
}
sort();
}
return true;
}
public void sort() {
Object[] a = toArray();
Arrays.sort( a );
ListIterator<E> i = listIterator();
for ( int j = 0; j < a.length; j++ )
{
i.next();
i.set( (E) a[ j ] );
}
}
public void sort( final Comparator c )
{
synchronized ( this )
{
Collections.sort( this, c );
}
}
/**
* Please refer to {@link java.util.List#indexOf(Object)} for more information regarding this function.
*/
@Override
public int indexOf( final Object o )
{
return o == null ? -1 : this.normalIndexOf( 0, this.size() - 1, (Comparable) o );
}
/**
* Please refer to {@link java.util.List#contains(Object)} for more information regarding this function.
*/
@Override
public boolean contains( final Object o )
{
return this.indexOf( o ) != -1;
}
/**
* A helper function which calculates the index of an element using binary search. In most cases, the difference is
* minimal, since most <code>ListModel</code> objects are fairly small. However, in the event that there are
* multiple <code>SortedListModel</code> objects of respectable size, having good performance is ideal.
*/
private int normalIndexOf( int beginIndex, int endIndex, final Comparable element )
{
int compareResult;
while ( true )
{
if ( beginIndex == endIndex )
{
compareResult = this.compare( element, (Comparable) this.get( beginIndex ) );
return compareResult == 0 ? beginIndex : -1;
}
if ( beginIndex > endIndex )
{
return -1;
}
// calculate the halfway point and compare the element with the
// element located at the halfway point - note that in locating
// the last index of, the value is rounded up to avoid an infinite
// recursive loop
int halfwayIndex = beginIndex + endIndex >> 1;
compareResult = this.compare( (Comparable) this.get( halfwayIndex ), element );
// if the element in the middle is larger than the element being checked,
// then it is known that the element is smaller than the middle element,
// so it must preceed the middle element
if ( compareResult > 0 )
{
endIndex = halfwayIndex - 1;
continue;
}
// if the element in the middle is smaller than the element being checked,
// then it is known that the element is larger than the middle element, so
// it must succeed the middle element
if ( compareResult < 0 )
{
beginIndex = halfwayIndex + 1;
continue;
}
// if the element in the middle is equal to the element being checked,
// then it is known that you have located at least one occurrence of the
// object; because duplicates are not allowed, return the halfway point
return halfwayIndex;
}
}
private int insertionIndexOf( int beginIndex, int endIndex, final Comparable element )
{
int compareResult;
while ( true )
{
if ( beginIndex == endIndex )
{
compareResult = this.compare( element, (Comparable) this.get( beginIndex ) );
return compareResult < 0 ? beginIndex : beginIndex + 1;
}
if ( beginIndex > endIndex )
{
return beginIndex;
}
// calculate the halfway point and compare the element with the
// element located at the halfway point - note that in locating
// the last index of, the value is rounded up to avoid an infinite
// recursive loop
int halfwayIndex = beginIndex + endIndex >> 1;
compareResult = this.compare( (Comparable) this.get( halfwayIndex ), element );
// if the element in the middle is larger than the element being checked,
// then it is known that the element is smaller than the middle element,
// so it must preceed the middle element
if ( compareResult > 0 )
{
endIndex = halfwayIndex - 1;
continue;
}
// if the element in the middle is smaller than the element being checked,
// then it is known that the element is larger than the middle element, so
// it must succeed the middle element
if ( compareResult < 0 )
{
beginIndex = halfwayIndex + 1;
continue;
}
// if the element in the middle is equal to the element being checked,
// then it is known that you have located at least one occurrence of the
// object; because duplicates are not allowed, return the halfway point
return halfwayIndex + 1;
}
}
private int compare( final Comparable left, final Comparable right )
{
return left instanceof String || right instanceof String ?
left.toString().compareToIgnoreCase( right.toString() ) :
left.compareTo( right );
}
}