//******************************************************************************************************
// RecordRestriction.cs - Gbtc
//
// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 03/13/2016 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using GSF.Collections;
namespace GSF.Data.Model
{
///
/// Defines a parameterized record restriction that can be applied to queries.
///
///
/// For versatility, values in the array are mutable, however, this makes the
/// array vulnerable to unintended updates for long-lived instances. Consequently, the normal use-case
/// of record restriction instances should be considered temporal. If an instance needs to be cached,
/// consider use of the function to reduce risk of unintended array updates.
///
public class RecordRestriction : IEquatable
{
#region [ Members ]
// Fields
///
/// Defines filter SQL expression for restriction as a composite format string - does not
/// include WHERE. When escaping is needed for field names, use standard ANSI quotes.
///
///
///
/// Each indexed parameter, e.g., "{0}", in the composite format string will be converted into
/// query parameters where each of the corresponding values in the
/// collection will be applied as values to an executed
/// query.
///
///
/// If needed, field names that are escaped with standard ANSI quotes in the filter expression
/// will be updated to reflect what is defined in the user model.
///
///
public readonly string FilterExpression;
///
/// Defines restriction parameter values.
///
///
/// If any of the reference a table field that is modeled with either an
/// or , then the function
/// will need to be called, replacing the
/// target parameter with the returned value so that the field value will be properly set prior
/// to executing any database function.
///
public readonly object[] Parameters;
#endregion
#region [ Constructors ]
///
/// Creates a new parameterized with the specified SQL filter
/// expression and parameters.
///
///
/// Filter SQL expression for restriction as a composite format string - does not include WHERE.
/// When escaping is needed for field names, use standard ANSI quotes.
///
/// Restriction parameter values.
///
///
/// Each indexed parameter, e.g., "{0}", in the composite format
/// will be converted into query parameters where each of the corresponding values in the
/// collection will be applied as
/// values to an executed query.
///
///
/// If any of the reference a table field that is modeled with either
/// an or , then the function
/// will need to be called, replacing the
/// target parameter with the returned value so that the field value will be properly set prior to
/// executing any database function.
///
///
/// If needed, field names that are escaped with standard ANSI quotes in the filter expression
/// will be updated to reflect what is defined in the user model.
///
///
public RecordRestriction(string filterExpression, params object[] parameters)
{
FilterExpression = filterExpression;
Parameters = parameters;
}
#endregion
#region [ Properties ]
///
/// Gets or sets field value for the specified .
///
/// Index into field array.
/// field value for the specified .
public object this[int index]
{
get
{
return Parameters[index];
}
set
{
Parameters[index] = value;
}
}
#endregion
#region [ Methods ]
///
/// Creates a deep copy of this record restriction.
///
/// Deep copy of this record restriction.
public RecordRestriction Clone()
{
return Clone(this);
}
///
/// Determines whether the specified object is equal to the current object.
///
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public override bool Equals(object obj)
{
RecordRestriction other = obj as RecordRestriction;
return (object)other != null && Equals(other);
}
///
/// Indicates whether the current object is equal to another object of the same type.
///
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(RecordRestriction other)
{
if (ReferenceEquals(this, other))
return true;
if (FilterExpression.Equals(other?.FilterExpression, StringComparison.Ordinal))
return Parameters.CompareTo(other?.Parameters, true) == 0;
return false;
}
///
/// Serves as the default hash function.
///
/// A hash code for the current .
public override int GetHashCode()
{
unchecked
{
return ((FilterExpression?.GetHashCode() ?? 0) * 397) ^ (Parameters?.GetHashCode() ?? 0);
}
}
#endregion
#region [ Operators ]
///
/// Implicitly converts a based filter expression into a .
///
/// Operand.
/// Record operation representing the specified filter expression.
public static implicit operator RecordRestriction(string value) => new RecordRestriction(value);
///
/// Compares to record restrictions for equality.
///
/// left operand.
/// right operand.
/// true if record restrictions are equal; otherwise, false.
public static bool operator ==(RecordRestriction left, RecordRestriction right)
{
if ((object)left == null && (object)right == null)
return true;
return left?.Equals(right) ?? false;
}
///
/// Compares to record restrictions for inequality.
///
/// left operand.
/// right operand.
/// true if record restrictions are not equal; otherwise, false.
public static bool operator !=(RecordRestriction left, RecordRestriction right)
{
if ((object)left == null && (object)right == null)
return false;
return !left?.Equals(right) ?? true;
}
///
/// Combines two record restrictions with an AND condition.
///
/// left operand.
/// right operand.
/// New combined record restriction.
///
/// If both parameters are null, result will be null. If one parameter is null
/// and the other parameter is not, the non-null parameter will be returned. Equally, if the
/// of both parameters are null, empty or only whitespace,
/// then the result will be null. If one parameter has a filter expression that is
/// null, empty or whitespace and the other parameter's filter expression is defined, then
/// the parameter that has a filter expression will be returned.
///
public static RecordRestriction operator +(RecordRestriction left, RecordRestriction right)
{
return CombineAnd(left, right);
}
///
/// Combines two record restrictions with an AND condition.
///
/// left operand.
/// right operand.
/// New combined record restriction.
///
/// If both parameters are null, result will be null. If one parameter is null
/// and the other parameter is not, the non-null parameter will be returned. Equally, if the
/// of both parameters are null, empty or only whitespace,
/// then the result will be null. If one parameter has a filter expression that is
/// null, empty or whitespace and the other parameter's filter expression is defined, then
/// the parameter that has a filter expression will be returned.
///
public static RecordRestriction operator &(RecordRestriction left, RecordRestriction right)
{
return CombineAnd(left, right);
}
///
/// Combines two record restrictions with an OR condition.
///
/// left operand.
/// right operand.
/// New combined record restriction.
///
/// If both parameters are null, result will be null. If one parameter is null
/// and the other parameter is not, the non-null parameter will be returned. Equally, if the
/// of both parameters are null, empty or only whitespace,
/// then the result will be null. If one parameter has a filter expression that is
/// null, empty or whitespace and the other parameter's filter expression is defined, then
/// the parameter that has a filter expression will be returned.
///
public static RecordRestriction operator |(RecordRestriction left, RecordRestriction right)
{
return CombineOr(left, right);
}
#endregion
#region [ Static ]
// Static Methods
///
/// Creates a deep copy of the record restriction.
///
/// Record restriction to clone.
/// Deep copy of the record restriction.
public static RecordRestriction Clone(RecordRestriction source)
{
if ((object)source == null)
return null;
object[] parameters = source.Parameters;
if ((object)parameters != null && parameters.Length > 0)
{
parameters = new object[source.Parameters.Length];
Array.Copy(source.Parameters, parameters, source.Parameters.Length);
}
return new RecordRestriction(source.FilterExpression, parameters);
}
///
/// Combines two record restrictions with an AND condition.
///
/// Left operand.
/// Right operand.
/// New combined record restriction.
///
/// If both parameters are null, result will be null. If one parameter is null
/// and the other parameter is not, the non-null parameter will be returned. Equally, if the
/// of both parameters are null, empty or only whitespace,
/// then the result will be null. If one parameter has a filter expression that is
/// null, empty or whitespace and the other parameter's filter expression is defined, then
/// the parameter that has a filter expression will be returned.
///
public static RecordRestriction CombineAnd(RecordRestriction left, RecordRestriction right)
{
return Combine(left, right, "AND");
}
///
/// Combines two record restrictions with an OR condition.
///
/// Left operand.
/// Right operand.
/// New combined record restriction.
///
/// If both parameters are null, result will be null. If one parameter is null
/// and the other parameter is not, the non-null parameter will be returned. Equally, if the
/// of both parameters are null, empty or only whitespace,
/// then the result will be null. If one parameter has a filter expression that is
/// null, empty or whitespace and the other parameter's filter expression is defined, then
/// the parameter that has a filter expression will be returned.
///
public static RecordRestriction CombineOr(RecordRestriction left, RecordRestriction right)
{
return Combine(left, right, "OR");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static RecordRestriction Combine(RecordRestriction left, RecordRestriction right, string operation)
{
// Check for null operands
if ((object)left == null && (object)right == null)
return null;
if ((object)left == null)
return right;
if ((object)right == null)
return left;
// Check for empty filter expressions
bool leftEmpty = string.IsNullOrWhiteSpace(left.FilterExpression);
bool rightEmpty = string.IsNullOrWhiteSpace(right.FilterExpression);
if (leftEmpty && rightEmpty)
return null;
if (leftEmpty)
return right;
if (rightEmpty)
return left;
// Check for missing parameters
int leftLength = left.Parameters?.Length ?? 0;
int rightLength = right.Parameters?.Length ?? 0;
if (leftLength == 0 && rightLength == 0)
return new RecordRestriction($"({left.FilterExpression}) {operation} ({right.FilterExpression})");
object[] parameters = leftLength == 0 ? right.Parameters : (rightLength == 0 ? left.Parameters : left.Parameters.Combine(right.Parameters));
object[] offsetArgs = Enumerable.Range(leftLength, rightLength).Select(index => (object)$"{{{index}}}").ToArray();
return new RecordRestriction($"({left.FilterExpression}) {operation} ({string.Format(right.FilterExpression, offsetArgs)})", parameters);
}
#endregion
}
}