/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
#region Using directives
using System;
using System.Collections;
using System.Management.Automation;
using System.Globalization;
#endregion
namespace Microsoft.PowerShell.Commands
{
#region PSObject Comparer
///
/// Keeps the property value of inputObject. Because the value of a non-existing property is null,
/// isExistingProperty is needed to distinguish whether a property exists and its value is null or
/// the property does not exist at all.
///
internal class ObjectCommandPropertyValue
{
private ObjectCommandPropertyValue() { }
internal ObjectCommandPropertyValue(object propVal)
{
PropertyValue = propVal;
IsExistingProperty = true;
}
///
/// ObjectCommandPropertyValue constructor.
///
/// Property Value.
/// Indicates if the Property value comparison has to be case sensitive or not.
/// Culture Info of the Property Value.
internal ObjectCommandPropertyValue(object propVal, bool isCaseSensitive, CultureInfo cultureInfo)
: this(propVal)
{
_caseSensitive = isCaseSensitive;
this.cultureInfo = cultureInfo;
}
internal object PropertyValue { get; }
internal bool IsExistingProperty { get; }
///
/// Indicates if the Property Value comparison has to be Case sensitive or not.
///
internal SwitchParameter CaseSensitive
{
get { return _caseSensitive; }
}
///
/// Gets the Culture Info of the Property Value.
///
internal CultureInfo Culture
{
get
{
return cultureInfo;
}
}
internal static readonly ObjectCommandPropertyValue NonExistingProperty = new ObjectCommandPropertyValue();
internal static readonly ObjectCommandPropertyValue ExistingNullProperty = new ObjectCommandPropertyValue(null);
private bool _caseSensitive;
internal CultureInfo cultureInfo = null;
///
/// Provides an Equals implementation.
///
/// Input Object.
/// True if both the objects are same or else returns false.
public override bool Equals(Object inputObject)
{
ObjectCommandPropertyValue objectCommandPropertyValueObject = inputObject as ObjectCommandPropertyValue;
if (objectCommandPropertyValueObject == null)
return false;
object baseObject = PSObject.Base(PropertyValue);
object inComingbaseObjectPropertyValue = PSObject.Base(objectCommandPropertyValueObject.PropertyValue);
IComparable baseObjectComparable = baseObject as IComparable;
if (baseObjectComparable != null)
{
return (LanguagePrimitives.Compare(baseObject, inComingbaseObjectPropertyValue, CaseSensitive, Culture) == 0);
}
else
{
if (baseObject == null && inComingbaseObjectPropertyValue == null)
{
return true;
}
if (baseObject != null && inComingbaseObjectPropertyValue != null)
{
return baseObject.ToString().Equals(inComingbaseObjectPropertyValue.ToString(), StringComparison.OrdinalIgnoreCase);
}
// One of the property values being compared is null.
return false;
}
}
///
/// Provides a GetHashCode() implementation.
///
/// Hashcode in the form of an integer.
public override int GetHashCode()
{
if (PropertyValue == null)
return 0;
object baseObject = PSObject.Base(PropertyValue);
IComparable baseObjectComparable = baseObject as IComparable;
if (baseObjectComparable != null)
{
return baseObject.GetHashCode();
}
else
{
return baseObject.ToString().GetHashCode();
}
}
}
///
///
///
internal class ObjectCommandComparer : IComparer
{
///
/// Constructor that doesn't set any private field.
/// Necessary because compareTo can compare two objects by calling
/// ((ICompare)obj1).CompareTo(obj2) without using a key.
///
internal ObjectCommandComparer(bool ascending, CultureInfo cultureInfo, bool caseSensitive)
{
_ascendingOrder = ascending;
_cultureInfo = cultureInfo ?? CultureInfo.CurrentCulture;
_caseSensitive = caseSensitive;
}
private static bool IsValueNull(object value)
{
object val = PSObject.Base(value);
return (val == null);
}
internal int Compare(ObjectCommandPropertyValue first, ObjectCommandPropertyValue second)
{
if (first.IsExistingProperty && second.IsExistingProperty)
{
return Compare(first.PropertyValue, second.PropertyValue);
}
// if first.IsExistingProperty, !second.IsExistingProperty; otherwise the
// first branch if would return. Regardless of key orders non existing property
// will be considered greater than others
if (first.IsExistingProperty)
{
return -1;
}
// vice versa for the previous branch
if (second.IsExistingProperty)
{
return 1;
}
//both are nonexisting
return 0;
}
///
/// Main method that will compare first and second by
/// their keys considering case and order
///
///
/// first object to extract value
///
///
/// second object to extract value
///
///
/// 0 if they are the same, less than 0 if first is smaller, more than 0 if first is greater
///
public int Compare(object first, object second)
{
// This method will never throw exceptions, two null
// objects are considered the same
if (IsValueNull(first) && IsValueNull(second)) return 0;
PSObject firstMsh = first as PSObject;
if (firstMsh != null)
{
first = firstMsh.BaseObject;
}
PSObject secondMsh = second as PSObject;
if (secondMsh != null)
{
second = secondMsh.BaseObject;
}
try
{
return LanguagePrimitives.Compare(first, second, !_caseSensitive, _cultureInfo) * (_ascendingOrder ? 1 : -1);
}
catch (InvalidCastException)
{
}
catch (ArgumentException)
{
// Note that this will occur if the objects do not support
// IComparable. We fall back to comparing as strings.
}
// being here means the first object doesn't support ICompare
// or an Exception was raised win Compare
string firstString = PSObject.AsPSObject(first).ToString();
string secondString = PSObject.AsPSObject(second).ToString();
return _cultureInfo.CompareInfo.Compare(firstString, secondString, _caseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase) * (_ascendingOrder ? 1 : -1);
}
private CultureInfo _cultureInfo = null;
private bool _ascendingOrder = true;
private bool _caseSensitive = false;
}
#endregion
}