/// <summary>
        /// Parses the search filter string.
        /// </summary>
        /// <param name="searchFilter">The search filter string.</param>
        public static GenericSearchCondition Parse( string searchFilter )
        {
            if( !string.IsNullOrEmpty( searchFilter ) )
            {
                var conditions = new List<GenericSearchCondition>();

                var searchIndex = 0;
                var markerIndex = -1;

                while( searchIndex < searchFilter.Length )
                {
                    var attrCondition = new GenericSearchAttributeCondition();
                    searchFilter = searchFilter.Substring( searchIndex ).Trim();

                    // --- AND ---
                    if( markerIndex != -1 )
                    {
                        if( searchFilter.StartsWith( "+" ) )
                            searchFilter = searchFilter.Substring( 1 ).Trim();
                        else
                            throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.AND" ), searchFilter ) );
                    }

                    // --- Attribute key ---
                    var opStr = FindFirst( searchFilter, AllOperationStrings );
                    if( opStr == null )
                        throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.Incomplete" ), searchFilter ) );

                    searchIndex = searchFilter.IndexOf( opStr, StringComparison.Ordinal );
                    markerIndex = searchIndex + opStr.Length;
                    var token = searchFilter.Substring( 0, searchIndex );

                    ushort key;
                    if( !ushort.TryParse( token, out key ) )
                        throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.NoKey" ), token ) );
                    attrCondition.Attribute = key;

                    // --- Operation ---
                    Operation op;
                    if( !Operations.TryGetValue( opStr, out op ) )
                        throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.InvalidOperator" ), token ) );
                    attrCondition.Operation = op;

                    // --- Parameters ---
                    searchIndex = searchFilter.IndexOf( '[', markerIndex );
                    if( searchIndex == -1 )
                        throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.NoValues" ), searchFilter ) );
                    if( searchFilter.Substring( markerIndex, searchIndex - markerIndex ).Trim() != String.Empty )
                        throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.Quotation" ), attrCondition.Operation ) );

                    markerIndex = searchIndex + 1;
                    searchIndex = searchFilter.IndexOf( ']', markerIndex );

                    if( searchIndex == -1 )
                    {
                        throw new InvalidOperationException( string.Format( ResourceManager.GetString( "ParsingError.NoClosingQuotes" ),
                            searchFilter.Substring( markerIndex ) ) );
                    }

                    token = searchFilter.Substring( markerIndex, searchIndex - markerIndex );

                    attrCondition.Value = token;
                    conditions.Add( attrCondition );

                    ++searchIndex;
                }
                return conditions.Count == 1 ? conditions[ 0 ] : new GenericSearchAnd { Conditions = conditions.ToArray() };
            }
            return null;
        }
        /// <summary>
        /// Parses the search filter string.
        /// </summary>
        /// <param name="searchFilter">The search filter string.</param>
        public static GenericSearchCondition Parse(string searchFilter)
        {
            if (!string.IsNullOrEmpty(searchFilter))
            {
                var conditions = new List <GenericSearchCondition>();

                var searchIndex = 0;
                var markerIndex = -1;

                while (searchIndex < searchFilter.Length)
                {
                    var attrCondition = new GenericSearchAttributeCondition();
                    searchFilter = searchFilter.Substring(searchIndex).Trim();

                    // --- AND ---
                    if (markerIndex != -1)
                    {
                        if (searchFilter.StartsWith("+"))
                        {
                            searchFilter = searchFilter.Substring(1).Trim();
                        }
                        else
                        {
                            throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.AND"), searchFilter));
                        }
                    }

                    // --- Attribute key ---
                    var opStr = FindFirst(searchFilter, AllOperationStrings);
                    if (opStr == null)
                    {
                        throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.Incomplete"), searchFilter));
                    }

                    searchIndex = searchFilter.IndexOf(opStr, StringComparison.Ordinal);
                    markerIndex = searchIndex + opStr.Length;
                    var token = searchFilter.Substring(0, searchIndex);

                    ushort key;
                    if (!ushort.TryParse(token, out key))
                    {
                        throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.NoKey"), token));
                    }
                    attrCondition.Attribute = key;

                    // --- Operation ---
                    Operation op;
                    if (!Operations.TryGetValue(opStr, out op))
                    {
                        throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.InvalidOperator"), token));
                    }
                    attrCondition.Operation = op;

                    // --- Parameters ---
                    searchIndex = searchFilter.IndexOf('[', markerIndex);
                    if (searchIndex == -1)
                    {
                        throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.NoValues"), searchFilter));
                    }
                    if (searchFilter.Substring(markerIndex, searchIndex - markerIndex).Trim() != String.Empty)
                    {
                        throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.Quotation"), attrCondition.Operation));
                    }

                    markerIndex = searchIndex + 1;
                    searchIndex = searchFilter.IndexOf(']', markerIndex);

                    if (searchIndex == -1)
                    {
                        throw new InvalidOperationException(string.Format(ResourceManager.GetString("ParsingError.NoClosingQuotes"),
                                                                          searchFilter.Substring(markerIndex)));
                    }

                    token = searchFilter.Substring(markerIndex, searchIndex - markerIndex);

                    attrCondition.Value = token;
                    conditions.Add(attrCondition);

                    ++searchIndex;
                }
                return(conditions.Count == 1 ? conditions[0] : new GenericSearchAnd {
                    Conditions = conditions.ToArray()
                });
            }
            return(null);
        }