public Expression Build(
            string searchParameterName,
            SearchModifierCode?modifier,
            SearchComparator comparator,
            int?componentIndex,
            ISearchValue searchValue)
        {
            EnsureArg.IsNotNullOrWhiteSpace(searchParameterName, nameof(searchParameterName));
            Debug.Assert(
                modifier == null || Enum.IsDefined(typeof(SearchModifierCode), modifier.Value),
                "Invalid modifier.");
            Debug.Assert(
                Enum.IsDefined(typeof(SearchComparator), comparator),
                "Invalid comparator.");
            EnsureArg.IsNotNull(searchValue, nameof(searchValue));

            _searchParameterName = searchParameterName;
            _modifier            = modifier;
            _comparator          = comparator;
            _componentIndex      = componentIndex;

            searchValue.AcceptVisitor(this);

            return(_outputExpression);
        }
Beispiel #2
0
        private void Validate(
            SearchParameter searchParameter,
            SearchModifierCode?modifier,
            string value,
            Action <Expression> valueValidator)
        {
            Expression expression = _parser.Parse(searchParameter, modifier, value);

            Assert.NotNull(expression);
            ValidateSearchParameterExpression(expression, DefaultParamName, valueValidator);
        }
Beispiel #3
0
        private Expression ParseSearchValueExpression(SearchParameterInfo searchParameter, string modifier, string value)
        {
            SearchModifierCode?parsedModifier = ParseSearchParamModifier();

            return(_searchParameterExpressionParser.Parse(searchParameter, parsedModifier, value));

            SearchModifierCode?ParseSearchParamModifier()
            {
                if (modifier == null)
                {
                    return(null);
                }

                if (SearchParamModifierMapping.TryGetValue(modifier, out SearchModifierCode searchModifierCode))
                {
                    return(searchModifierCode);
                }

                throw new InvalidSearchOperationException(
                          string.Format(Core.Resources.ModifierNotSupported, modifier, searchParameter.Name));
            }
        }
Beispiel #4
0
        public Expression Parse(
            SearchParameterInfo searchParameter,
            SearchModifierCode?modifier,
            string value)
        {
            EnsureArg.IsNotNull(searchParameter, nameof(searchParameter));

            Debug.Assert(
                modifier == null || Enum.IsDefined(typeof(SearchModifierCode), modifier.Value),
                "Invalid modifier.");
            EnsureArg.IsNotNullOrWhiteSpace(value, nameof(value));

            Expression outputExpression;

            if (modifier == SearchModifierCode.Missing)
            {
                // We have to handle :missing modifier specially because if :missing modifier is specified,
                // then the value is a boolean string indicating whether the parameter is missing or not instead of
                // the search value type associated with the search parameter.
                if (!bool.TryParse(value, out bool isMissing))
                {
                    // An invalid value was specified.
                    throw new InvalidSearchOperationException(Core.Resources.InvalidValueTypeForMissingModifier);
                }

                return(Expression.MissingSearchParameter(searchParameter, isMissing));
            }

            if (modifier == SearchModifierCode.Text)
            {
                // We have to handle :text modifier specially because if :text modifier is supplied for token search param,
                // then we want to search the display text using the specified text, and therefore
                // we don't want to actually parse the specified text into token.
                if (searchParameter.Type != ValueSets.SearchParamType.Token)
                {
                    throw new InvalidSearchOperationException(
                              string.Format(CultureInfo.InvariantCulture, Core.Resources.ModifierNotSupported, modifier, searchParameter.Name));
                }

                outputExpression = Expression.Contains(FieldName.TokenText, null, value, true);
            }
            else
            {
                // Build the expression for based on the search value.
                if (searchParameter.Type == ValueSets.SearchParamType.Composite)
                {
                    if (modifier != null)
                    {
                        throw new InvalidSearchOperationException(
                                  string.Format(CultureInfo.InvariantCulture, Core.Resources.ModifierNotSupported, modifier, searchParameter.Name));
                    }

                    IReadOnlyList <string> compositeValueParts = value.SplitByCompositeSeparator();

                    if (compositeValueParts.Count > searchParameter.Component.Count)
                    {
                        throw new InvalidSearchOperationException(
                                  string.Format(CultureInfo.InvariantCulture, Core.Resources.NumberOfCompositeComponentsExceeded, searchParameter.Name));
                    }

                    var compositeExpressions = new Expression[compositeValueParts.Count];

                    var searchParameterComponentInfos = searchParameter.Component.ToList();

                    for (int i = 0; i < compositeValueParts.Count; i++)
                    {
                        var component = searchParameterComponentInfos[i];

                        // Find the corresponding search parameter info.
                        SearchParameterInfo componentSearchParameter = _searchParameterDefinitionManager.GetSearchParameter(component.DefinitionUrl);

                        string componentValue = compositeValueParts[i];

                        compositeExpressions[i] = Build(
                            componentSearchParameter,
                            modifier: null,
                            componentIndex: i,
                            value: componentValue);
                    }

                    outputExpression = Expression.And(compositeExpressions);
                }
                else
                {
                    outputExpression = Build(
                        searchParameter,
                        modifier,
                        componentIndex: null,
                        value: value);
                }
            }

            return(Expression.SearchParameter(searchParameter, outputExpression));
        }
Beispiel #5
0
        private Expression Build(
            SearchParameterInfo searchParameter,
            SearchModifierCode?modifier,
            int?componentIndex,
            string value)
        {
            ReadOnlySpan <char> valueSpan = value.AsSpan();

            // By default, the comparator is equal.
            SearchComparator comparator = SearchComparator.Eq;

            if (searchParameter.Type == ValueSets.SearchParamType.Date ||
                searchParameter.Type == ValueSets.SearchParamType.Number ||
                searchParameter.Type == ValueSets.SearchParamType.Quantity)
            {
                // If the search parameter type supports comparator, parse the comparator (if present).
                Tuple <string, SearchComparator> matchedComparator = SearchParamComparators.FirstOrDefault(
                    s => value.StartsWith(s.Item1, StringComparison.Ordinal));

                if (matchedComparator != null)
                {
                    comparator = matchedComparator.Item2;
                    valueSpan  = valueSpan.Slice(matchedComparator.Item1.Length);
                }
            }

            // Parse the value.
            Func <string, ISearchValue> parser = _parserDictionary[Enum.Parse <SearchParamType>(searchParameter.Type?.ToString())];

            // Build the expression.
            var helper = new SearchValueExpressionBuilderHelper();

            // If the value contains comma, then we need to convert it into in expression.
            // But in this case, the user cannot specify prefix.
            IReadOnlyList <string> parts = value.SplitByOrSeparator();

            if (parts.Count == 1)
            {
                // This is a single value expression.
                ISearchValue searchValue = parser(valueSpan.ToString());

                return(helper.Build(
                           searchParameter.Name,
                           modifier,
                           comparator,
                           componentIndex,
                           searchValue));
            }
            else
            {
                if (comparator != SearchComparator.Eq)
                {
                    throw new InvalidSearchOperationException(Core.Resources.SearchComparatorNotSupported);
                }

                // This is a multiple value expression.
                Expression[] expressions = parts.Select(part =>
                {
                    ISearchValue searchValue = parser(part);

                    return(helper.Build(
                               searchParameter.Name,
                               modifier,
                               comparator,
                               componentIndex,
                               searchValue));
                }).ToArray();

                return(Expression.Or(expressions));
            }
        }