/*
*
* 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.
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
namespace Lucene.Net.Support
{
/// Represents a strongly typed list of objects that can be accessed by index.
/// Provides methods to search, sort, and manipulate lists. Also provides functionality
/// to compare lists against each other through an implementations of
/// .
/// The type of elements in the list.
[Serializable]
public class EquatableList : System.Collections.Generic.List,
IEquatable>,
ICloneable
{
/// Initializes a new instance of the
/// class that is empty and has the
/// default initial capacity.
public EquatableList() : base() { }
/// Initializes a new instance of the
/// class that contains elements copied from the specified collection and has
/// sufficient capacity to accommodate the number of elements copied.
/// The collection whose elements are copied to the new list.
public EquatableList(System.Collections.Generic.IEnumerable collection) : base(collection) { }
/// Initializes a new instance of the
/// class that is empty and has the specified initial capacity.
/// The number of elements that the new list can initially store.
public EquatableList(int capacity) : base(capacity) { }
/// Adds a range of objects represented by the
/// implementation.
/// The
/// implementation to add to this list.
public void AddRange(ICollection c)
{
// If the collection is null, throw an exception.
if (c == null) throw new ArgumentNullException("c");
// Pre-compute capacity.
Capacity = Math.Max(c.Count + Count, Capacity);
// Cycle through the items and add.
foreach (T item in c)
{
// Add the item.
Add(item);
}
}
/// Compares the counts of two
/// implementations.
/// This uses a trick in LINQ, sniffing types for implementations
/// of interfaces that might supply shortcuts when trying to make comparisons.
/// In this case, that is the and
/// interfaces, either of which can provide a count
/// which can be used in determining the equality of sequences (if they don't have
/// the same count, then they can't be equal).
/// The from the left hand side of the
/// comparison to check the count of.
/// The from the right hand side of the
/// comparison to check the count of.
/// Null if the result is indeterminate. This occurs when either
/// or doesn't implement or .
/// Otherwise, it will get the count from each and return true if they are equal, false otherwise.
private static bool? EnumerableCountsEqual(System.Collections.Generic.IEnumerable x, System.Collections.Generic.IEnumerable y)
{
// Get the ICollection and ICollection interfaces.
System.Collections.Generic.ICollection xOfTCollection = x as System.Collections.Generic.ICollection;
System.Collections.Generic.ICollection yOfTCollection = y as System.Collections.Generic.ICollection;
ICollection xCollection = x as ICollection;
ICollection yCollection = y as ICollection;
// The count in x and y.
int? xCount = xOfTCollection != null ? xOfTCollection.Count : xCollection != null ? xCollection.Count : (int?)null;
int? yCount = yOfTCollection != null ? yOfTCollection.Count : yCollection != null ? yCollection.Count : (int?)null;
// If either are null, return null, the result is indeterminate.
if (xCount == null || yCount == null)
{
// Return null, indeterminate.
return null;
}
// Both counts are non-null, compare.
return xCount == yCount;
}
/// Compares the contents of a
/// implementation to another one to determine equality.
/// Thinking of the implementation as
/// a string with any number of characters, the algorithm checks
/// each item in each list. If any item of the list is not equal (or
/// one list contains all the elements of another list), then that list
/// element is compared to the other list element to see which
/// list is greater.
/// The implementation
/// that is considered the left hand side.
/// The implementation
/// that is considered the right hand side.
/// True if the items are equal, false otherwise.
private static bool Equals(System.Collections.Generic.IEnumerable x,
System.Collections.Generic.IEnumerable y)
{
// If x and y are null, then return true, they are the same.
if (x == null && y == null)
{
// They are the same, return 0.
return true;
}
// If one is null, then return a value based on whether or not
// one is null or not.
if (x == null || y == null)
{
// Return false, one is null, the other is not.
return false;
}
// Check to see if the counts on the IEnumerable implementations are equal.
// This is a shortcut, if they are not equal, then the lists are not equal.
// If the result is indeterminate, then get out.
bool? enumerableCountsEqual = EnumerableCountsEqual(x, y);
// If the enumerable counts have been able to be calculated (indicated by
// a non-null value) and it is false, then no need to iterate through the items.
if (enumerableCountsEqual != null && !enumerableCountsEqual.Value)
{
// The sequences are not equal.
return false;
}
// The counts of the items in the enumerations are equal, or indeterminate
// so a full iteration needs to be made to compare each item.
// Get the default comparer for T first.
System.Collections.Generic.EqualityComparer defaultComparer =
EqualityComparer.Default;
// Get the enumerator for y.
System.Collections.Generic.IEnumerator otherEnumerator = y.GetEnumerator();
// Call Dispose on IDisposable if there is an implementation on the
// IEnumerator returned by a call to y.GetEnumerator().
using (otherEnumerator as IDisposable)
{
// Cycle through the items in this list.
foreach (T item in x)
{
// If there isn't an item to get, then this has more
// items than that, they are not equal.
if (!otherEnumerator.MoveNext())
{
// Return false.
return false;
}
// Perform a comparison. Must check this on the left hand side
// and that on the right hand side.
bool comparison = defaultComparer.Equals(item, otherEnumerator.Current);
// If the value is false, return false.
if (!comparison)
{
// Return the value.
return comparison;
}
}
// If there are no more items, then return true, the sequences
// are equal.
if (!otherEnumerator.MoveNext())
{
// The sequences are equal.
return true;
}
// The other sequence has more items than this one, return
// false, these are not equal.
return false;
}
}
#region IEquatable> Members
/// Compares this sequence to another
/// implementation, returning true if they are equal, false otherwise.
/// The other implementation
/// to compare against.
/// True if the sequence in
/// is the same as this one.
public bool Equals(System.Collections.Generic.IEnumerable other)
{
// Compare to the other sequence. If 0, then equal.
return Equals(this, other);
}
#endregion
/// Compares this object for equality against other.
/// The other object to compare this object against.
/// True if this object and are equal, false
/// otherwise.
public override bool Equals(object obj)
{
// Call the strongly typed version.
return Equals(obj as System.Collections.Generic.IEnumerable);
}
/// Gets the hash code for the list.
/// The hash code value.
public override int GetHashCode()
{
// Call the static method, passing this.
return GetHashCode(this);
}
#if __MonoCS__
public static int GetHashCode(System.Collections.Generic.IEnumerable source)
#else
/// Gets the hash code for the list.
/// The
/// implementation which will have all the contents hashed.
/// The hash code value.
public static int GetHashCode(System.Collections.Generic.IEnumerable source)
#endif
{
// If source is null, then return 0.
if (source == null) return 0;
// Seed the hash code with the hash code of the type.
// This is done so that you don't have a lot of collisions of empty
// ComparableList instances when placed in dictionaries
// and things that rely on hashcodes.
int hashCode = typeof(T).GetHashCode();
// Iterate through the items in this implementation.
foreach (T item in source)
{
// Adjust the hash code.
hashCode = 31 * hashCode + (item == null ? 0 : item.GetHashCode());
}
// Return the hash code.
return hashCode;
}
// TODO: When diverging from Java version of Lucene, can uncomment these to adhere to best practices when overriding the Equals method and implementing IEquatable.
///// Overload of the == operator, it compares a
///// to an
///// implementation.
///// The to compare
///// against .
///// The to compare
///// against .
///// True if the instances are equal, false otherwise.
//public static bool operator ==(EquatableList x, System.Collections.Generic.IEnumerable y)
//{
// // Call Equals.
// return Equals(x, y);
//}
///// Overload of the == operator, it compares a
///// to an
///// implementation.
///// The to compare
///// against .
///// The to compare
///// against .
///// True if the instances are equal, false otherwise.
//public static bool operator ==(System.Collections.Generic.IEnumerable x, EquatableList y)
//{
// // Call equals.
// return Equals(x, y);
//}
///// Overload of the != operator, it compares a
///// to an
///// implementation.
///// The to compare
///// against .
///// The to compare
///// against .
///// True if the instances are not equal, false otherwise.
//public static bool operator !=(EquatableList x, System.Collections.Generic.IEnumerable y)
//{
// // Return the negative of the equals operation.
// return !(x == y);
//}
///// Overload of the != operator, it compares a
///// to an
///// implementation.
///// The to compare
///// against .
///// The to compare
///// against .
///// True if the instances are not equal, false otherwise.
//public static bool operator !=(System.Collections.Generic.IEnumerable x, EquatableList y)
//{
// // Return the negative of the equals operation.
// return !(x == y);
//}
#region ICloneable Members
/// Clones the .
/// This is a shallow clone.
/// A new shallow clone of this
/// .
public object Clone()
{
// Just create a new one, passing this to the constructor.
return new EquatableList(this);
}
#endregion
}
}