private void Initialize(System.Text.RegularExpressions.Group left, System.Text.RegularExpressions.Group @operator, System.Text.RegularExpressions.Group right) { if (null == left) { throw new ArgumentNullException(nameof(left)); } if (null == @operator) { throw new ArgumentNullException(nameof(@operator)); } if (null == right) { throw new ArgumentNullException(nameof(right)); } if ( !left.Success || !right.Success || string.IsNullOrEmpty(left.Value) || string.IsNullOrEmpty(right.Value) ) { string message = CultureInfo.InvariantCulture + "ExceptionInvalidFilterTemplate" + this.Text; throw new InvalidOperationException(message); } this.attributePath = left.Value; if (!Enum.TryParse <ComparisonOperatorValue>(@operator.Value, out ComparisonOperatorValue comparisonOperatorValue)) { string message = CultureInfo.InvariantCulture + "ExceptionInvalidFilterTemplate" + this.Text; throw new InvalidOperationException(message); } this.Operator = comparisonOperatorValue; if (!FilterExpression.TryParse(right.Value, out string comparisonValue)) { string message = CultureInfo.InvariantCulture + "ExceptionInvalidFilterTemplate" + this.Text; throw new InvalidOperationException(message); } this.value = new ComparisonValue(comparisonValue, FilterExpression.Quote == right.Value[0]); int indexRemainder = right.Value.IndexOf(comparisonValue, StringComparison.Ordinal) + comparisonValue.Length; if (indexRemainder >= right.Value.Length) { return; } string remainder = right.Value.Substring(indexRemainder); int indexAnd = remainder.IndexOf(FilterExpression.LogicalOperatorAnd.Value, StringComparison.Ordinal); int indexOr = remainder.IndexOf(FilterExpression.LogicalOperatorOr.Value, StringComparison.Ordinal); int indexNextFilter; int indexLogicalOperator; if (indexAnd >= 0 && (indexOr < 0 || indexAnd < indexOr)) { indexNextFilter = indexAnd + FilterExpression.LogicalOperatorAnd.Value.Length; this.logicalOperator = LogicalOperatorValue.and; indexLogicalOperator = indexAnd; } else if (indexOr >= 0) { indexNextFilter = indexOr + FilterExpression.LogicalOperatorOr.Value.Length; this.logicalOperator = LogicalOperatorValue.or; indexLogicalOperator = indexOr; } else { string tail = remainder.Trim().TrimEnd(FilterExpression.TrailingCharacters.Value); if (!string.IsNullOrWhiteSpace(tail)) { string message = CultureInfo.InvariantCulture + "ExceptionInvalidFilterTemplate" + this.Text; throw new InvalidOperationException(message); } else { return; } } string nextExpression = remainder.Substring(indexNextFilter); int indexClosingBracket = remainder.IndexOf(FilterExpression.BracketClose, StringComparison.Ordinal); int nextExpressionLevel; int nextExpressionGroup; if (indexClosingBracket >= 0 && indexClosingBracket < indexLogicalOperator) { nextExpressionLevel = this.Level - 1; nextExpressionGroup = this.Group - 1; } else { nextExpressionLevel = this.Level; nextExpressionGroup = this.Group; } this.next = new FilterExpression(nextExpression, nextExpressionGroup, nextExpressionLevel); this.next.Previous = this; }
public IReadOnlyCollection <IFilter> ToFilters() { IReadOnlyCollection <IFilter> result = new FilterExpression(this).Convert(); return(result); }
// Convert the doubly-linked list into a collection of IFilter objects. // There are three cases that may be encountered as the conversion proceeds through the linked list of clauses. // Those cases are documented by comments below. private IReadOnlyCollection <IFilter> Convert() { List <IFilter> result = new List <IFilter>(); IFilter thisFilter = this.ToFilter(); result.Add(thisFilter); FilterExpression current = this.next; while (current != null) { if (this.Level == current.Level) { // The current clause has the same level number as the initial clause, // such as // b eq 2 // in the expression // a eq 1 and b eq 2. IFilter filter = current.ToFilter(); switch (current.Previous.logicalOperator) { case LogicalOperatorValue.and: IFilter left = result.Last(); FilterExpression.And(left, filter); break; case LogicalOperatorValue.or: result.Add(filter); break; default: string notSupported = Enum.GetName(typeof(LogicalOperatorValue), this.logicalOperator); throw new NotSupportedException(notSupported); } current = current.next; } else if (this.Level > current.Level) { // The current clause has a lower level number than the initial clause, // such as // c eq 3 // in the expression // (a eq 1 and b eq 2) or c eq 3. IReadOnlyCollection <IFilter> superiors = current.Convert(); switch (current.Previous.logicalOperator) { case LogicalOperatorValue.and: IFilter superior = superiors.First(); result = FilterExpression.And(result, superior).ToList(); IReadOnlyCollection <IFilter> remainder = superiors.Skip(1).ToArray(); result.AddRange(remainder); break; case LogicalOperatorValue.or: result.AddRange(superiors); break; default: string notSupported = Enum.GetName(typeof(LogicalOperatorValue), this.logicalOperator); throw new NotSupportedException(notSupported); } break; } else { // The current clause has a higher level number than the initial clause, // such as // b eq 2 // in the expression // a eq 1 and (b eq 2 or c eq 3) and (d eq 4 or e eq 5) // // In this case, the linked list is edited, // so that // c eq 3 // has no next link, // while the next link of // a eq 1 // refers to // d eq 4. // Thereby, // b eq 2 or c eq 3 // can be converted to filters and combined with the filter composed from // a eq 1, // after which conversion will continue with the conversion of // d eq 4. // It is the change in group number between // c eq 3 // and // d eq 4 // that identifies the end of current group, // despite the two clauses having the same level number. // // It is because of the editing of the linked list that the public method, // ToFilters(), // makes a copy of the linked list before initiating conversion; // so that, // ToFilters() // can be called on a FilterExpression any number of times, // to yield the same output. FilterExpression subordinate = current; while (current != null && this.Level < current.Level && subordinate.Group == current.Group) { current = current.next; } if (current != null) { current.Previous.next = null; subordinate.Previous.next = current; } IReadOnlyCollection <IFilter> subordinates = subordinate.Convert(); switch (subordinate.Previous.logicalOperator) { case LogicalOperatorValue.and: IFilter superior = result.Last(); IReadOnlyCollection <IFilter> merged = FilterExpression.And(superior, subordinates); result.AddRange(merged); break; case LogicalOperatorValue.or: result.AddRange(subordinates); break; default: string notSupported = Enum.GetName(typeof(LogicalOperatorValue), this.logicalOperator); throw new NotSupportedException(notSupported); } } } return(result); }