//---------------------------------------------------------------------------------------- /// <summary> /// Return a List of custom objects filtered by the filter parameter. /// </summary> /// <param name="filter">A FilterExpression representing the desired filter.</param> /// <returns>A new List of custom objects with the requested Records</returns> /// public List <T> SelectRecords <T>(FilterExpression filter) where T : class, new() { return(SelectRecords <T>(filter, null, null, false)); }
//---------------------------------------------------------------------------------------- /// <summary> /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields /// will be in the Table. /// </summary> /// <param name="filter">A FilterExpression representing the desired filter.</param> /// <param name="fieldList">The desired fields to be in the returned Table</param> /// <param name="orderByList">A list of one or more fields to order the returned table by, /// or null for default order. If an orderByField is prefixed with "!", that field will sorted /// in reverse order.</param> /// <returns>A new List of custom objects with the requested Records and Fields ordered by the specified fields.</returns> /// public List <T> SelectRecords <T>(FilterExpression filter, string[] fieldList, string[] orderByList) where T : class, new() { return(SelectRecords <T>(filter, fieldList, orderByList, false)); }
static void parseExpression(string filter, ref int pos, FilterExpressionGroup parentSrchExpGrp) { ParseState state = ParseState.Left; bool hasBracket = false, inString = false, isNot = false; string fieldName = null; object searchVal = null; var sbTemp = new StringBuilder(); ComparisonOperatorEnum comparisonOp = ComparisonOperatorEnum.Equal; MatchTypeEnum matchType = MatchTypeEnum.UseCase; BoolOpEnum curBoolOp = BoolOpEnum.And; int startPos = pos; // skip past any leading spaces while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } for ( ; pos < filter.Length;) { ////////////////////////////////////////////////////////// #region Left if (state == ParseState.Left) { if (filter[pos] == '[') // field names with ' ' in them must be wrapped with brackets { hasBracket = true; pos++; startPos = pos; } if (hasBracket) { // look for ending bracket if (filter[pos] == ']') { fieldName = filter.Substring(startPos, pos - startPos).Trim(); pos++; // skip past bracket } } else // no bracket - look for non-alpha { if (filter[pos] == '(') { // start of a new FilterExpressionGroup pos++; var newSrchExpGrp = new FilterExpressionGroup(); parentSrchExpGrp.Add(curBoolOp, newSrchExpGrp); parseExpression(filter, ref pos, newSrchExpGrp); state = ParseState.BoolOp; } else if (filter[pos] == '~') // eg. ~LastName { matchType = MatchTypeEnum.IgnoreCase; } else if (char.IsWhiteSpace(filter[pos]) || (!char.IsLetterOrDigit(filter[pos]) && filter[pos] != '_' && filter[pos] != '~')) // field names with spaces in them must be wrapped with brackets { fieldName = filter.Substring(startPos, pos - startPos).Trim(); } } if (fieldName != null) { if (fieldName[0] == '~') { fieldName = fieldName.Substring(1); matchType = MatchTypeEnum.IgnoreCase; } state = ParseState.CompareOp; } else { pos++; } } #endregion Left ////////////////////////////////////////////////////////// #region CompareOp else if (state == ParseState.CompareOp) { // skip whitespace while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } if (char.IsLetter(filter[pos])) // REGEX { // should be CONTAINS, REGEX, IN or NOT //if( pos + 4 >= filter.Length ) // throwInvalidFilterConstruct( filter, pos ); try { // NOT if (char.ToUpper(filter[pos]) == 'N' && char.ToUpper(filter[pos + 1]) == 'O' && char.ToUpper(filter[pos + 2]) == 'T' && char.IsWhiteSpace(filter[pos + 3])) { pos += 3; isNot = true; continue; } // IN else if (char.ToUpper(filter[pos]) == 'I' && char.ToUpper(filter[pos + 1]) == 'N' && (char.IsWhiteSpace(filter[pos + 2]) || filter[pos + 2] == '(')) { pos += 2; if (char.IsWhiteSpace(filter[pos])) // skip whitespace { while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } if (filter[pos] != '(') { throwInvalidFilterConstruct(filter, pos - 2); } } comparisonOp = ComparisonOperatorEnum.In; } // REGEX else if (char.ToUpper(filter[pos]) == 'R' && char.ToUpper(filter[pos + 1]) == 'E' && char.ToUpper(filter[pos + 2]) == 'G' && char.ToUpper(filter[pos + 3]) == 'E' && char.ToUpper(filter[pos + 4]) == 'X' && char.IsWhiteSpace(filter[pos + 5])) { pos += 5; comparisonOp = ComparisonOperatorEnum.Regex; } // CONTAINS else if (char.ToUpper(filter[pos]) == 'C' && char.ToUpper(filter[pos + 1]) == 'O' && char.ToUpper(filter[pos + 2]) == 'N' && char.ToUpper(filter[pos + 3]) == 'T' && char.ToUpper(filter[pos + 4]) == 'A' && char.ToUpper(filter[pos + 5]) == 'I' && char.ToUpper(filter[pos + 6]) == 'N' && char.ToUpper(filter[pos + 7]) == 'S' && char.IsWhiteSpace(filter[pos + 8])) { pos += 8; comparisonOp = ComparisonOperatorEnum.Contains; } else { throwInvalidFilterConstruct(filter, pos - 2); } } catch (Exception ex) { throwInvalidFilterConstruct(filter, pos - 2); } } // alternative way to specify ignore case search (other way is to prefix a fieldname with ~) else if (filter[pos] == '~') // ~= { matchType = MatchTypeEnum.IgnoreCase; if (++pos >= filter.Length) { throwInvalidFilterConstruct(filter, pos); } // next char must be = if (filter[pos] != '=') { throwInvalidFilterConstruct(filter, pos); } comparisonOp = ComparisonOperatorEnum.Equal; } else if (filter[pos] == '!') // != { if (++pos >= filter.Length) { throwInvalidFilterConstruct(filter, pos); } // next char must be = if (filter[pos] != '=') { throwInvalidFilterConstruct(filter, pos); } comparisonOp = ComparisonOperatorEnum.Equal; isNot = true; } else if (filter[pos] == '=') { comparisonOp = ComparisonOperatorEnum.Equal; } else if (filter[pos] == '<') // <, <= or <> { if (pos + 1 >= filter.Length) { throwInvalidFilterConstruct(filter, pos); } if (filter[pos + 1] == '>') { pos++; comparisonOp = ComparisonOperatorEnum.Equal; isNot = true; } else if (filter[pos + 1] == '=') { pos++; comparisonOp = ComparisonOperatorEnum.LessThanOrEqual; } else { comparisonOp = ComparisonOperatorEnum.LessThan; } } else if (filter[pos] == '>') // > or >= { if (pos + 1 >= filter.Length) { throwInvalidFilterConstruct(filter, pos); } if (filter[pos + 1] == '=') { pos++; comparisonOp = ComparisonOperatorEnum.GreaterThanOrEqual; } else { comparisonOp = ComparisonOperatorEnum.GreaterThan; } } else { throwInvalidFilterConstruct(filter, pos); } pos++; state = ParseState.Right; } #endregion CompareOp ////////////////////////////////////////////////////////// #region Right else if (state == ParseState.Right) { if (comparisonOp == ComparisonOperatorEnum.In) //|| comparisonOp == EqualityEnum.NotIn ) { // skip whitespace while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } // filter[pos] should look like this now: (val1, val2, val3) // or like this: ('val1', 'val2', 'val3') if (filter[pos] == '(') { pos++; } // find the end int endPos = pos; while (endPos < filter.Length && filter[endPos] != ')') { endPos++; } if (endPos >= filter.Length) { throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), FileDbExceptionsEnum.InvalidFilterConstruct); } string inVals = filter.Substring(pos, endPos - pos); searchVal = parseInVals(inVals, matchType); pos = endPos; } else { if (!inString) { // skip whitespace only if we haven't found anything yet if (sbTemp.Length == 0) { while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } } // look for end of ExpressionGroup if (sbTemp.Length > 0 && (filter[pos] == ')' || char.IsWhiteSpace(filter[pos]))) { // Expression completed searchVal = sbTemp.ToString(); sbTemp.Length = 0; var srchExp = new FilterExpression(fieldName, searchVal, comparisonOp, matchType, isNot); parentSrchExpGrp.Add(curBoolOp, srchExp); if (filter[pos] == ')') { return; } fieldName = null; state = ParseState.BoolOp; } else if (sbTemp.Length == 0 && filter[pos] == '\'') { // just starting to get the value inString = /*isString=*/ true; } else { sbTemp.Append(filter[pos]); } } else // inString == true { if (filter[pos] == '\'') { //Debug.Assert( sbTemp.Length > 0 ); -- it could be empty, eg. myfield = '' // if the next char is NOT another ' (escaped) then the string is completed if ((pos + 1 < filter.Length) && filter[pos + 1] == '\'') { pos++; } else { inString = false; searchVal = sbTemp.ToString(); sbTemp.Length = 0; var srchExp = new FilterExpression(fieldName, searchVal, comparisonOp, matchType, isNot); parentSrchExpGrp.Add(curBoolOp, srchExp); fieldName = null; state = ParseState.BoolOp; goto Advance; } } sbTemp.Append(filter[pos]); } } Advance: // advance pos++; } #endregion Right ////////////////////////////////////////////////////////// #region Next else // if( state == ParseState.BoolOp ) { Debug.Assert(state == ParseState.BoolOp); if (sbTemp.Length == 0) { // skip whitespace while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } } if (filter[pos] == ')') { return; // we must be finished } if (char.IsWhiteSpace(filter[pos])) { // we must be finished if (sbTemp.Length == 0) { throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), FileDbExceptionsEnum.InvalidFilterConstruct); } string sOp = sbTemp.ToString(); sbTemp.Length = 0; if (string.Compare(sOp, "AND", StringComparison.OrdinalIgnoreCase) == 0) { curBoolOp = BoolOpEnum.And; } else if (string.Compare(sOp, "OR", StringComparison.OrdinalIgnoreCase) == 0) { curBoolOp = BoolOpEnum.Or; } else { throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), FileDbExceptionsEnum.InvalidFilterConstruct); } state = ParseState.Left; // start over on next expression // skip whitespace while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) { pos++; } // reset vars startPos = pos; hasBracket = false; } else { sbTemp.Append(filter[pos]); pos++; } } #endregion Next } // for... // did we just complete an Expression? if (state == ParseState.Right) { if (comparisonOp != ComparisonOperatorEnum.In) //&& comparisonOp != EqualityEnum.NotIn ) { searchVal = sbTemp.ToString(); if (!inString && string.Compare((string)searchVal, "null", StringComparison.OrdinalIgnoreCase) == 0) { searchVal = null; } sbTemp.Length = 0; } var srchExp = new FilterExpression(fieldName, searchVal, comparisonOp, matchType, isNot); parentSrchExpGrp.Add(curBoolOp, srchExp); } }