/* * * 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 } }