Пример #1
0
        /// <summary>
        /// Finds the signature that best matches the arguments
        /// </summary>
        /// <param name="functionName">The name of the function</param>
        /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param>
        /// <param name="signatures">The signatures to match against</param>
        /// <returns>Returns the matching signature or throws</returns>
        internal static FunctionSignatureWithReturnType MatchSignatureToBuiltInFunction(string functionName, SingleValueNode[] argumentNodes, FunctionSignatureWithReturnType[] signatures)
        {
            FunctionSignatureWithReturnType signature;

            IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray();

            // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments
            int argumentCount = argumentTypes.Length;

            if (argumentTypes.All(a => a == null) && argumentCount > 0)
            {
                // we specifically want to find just the first function that matches the number of arguments, we don't care about
                // ambiguity here because we're already in an ambiguous case where we don't know what kind of types
                // those arguments are.
                signature = signatures.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == argumentCount);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionName, argumentTypes.Count()));
                }
                else
                {
                    // in this case we can't assert the return type, we can only assert that a function exists... so
                    // we need to set the return type to null.
                    signature = new FunctionSignatureWithReturnType(null, signature.ArgumentTypes);
                }
            }
            else
            {
                signature = TypePromotionUtils.FindBestFunctionSignature(signatures, argumentNodes);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                                                 functionName,
                                                 BuiltInFunctions.BuildFunctionSignatureListDescription(functionName, signatures)));
                }
            }

            return(signature);
        }
Пример #2
0
        /// <summary>
        /// Ensure that the segments given to us are valid select segments.
        /// </summary>
        /// <exception cref="ODataException">Throws if the list of segments doesn't match the requirements for a path in $select</exception>
        private void ValidatePath()
        {
            int index = 0;

            foreach (ODataPathSegment segment in this)
            {
                if (segment is NavigationPropertySegment)
                {
                    if (index != this.Count - 1)
                    {
                        throw new ODataException(ODataErrorStrings.ODataSelectPath_NavPropSegmentCanOnlyBeLastSegment);
                    }
                }
                else if (segment is OperationSegment)
                {
                    if (index != this.Count - 1)
                    {
                        throw new ODataException(ODataErrorStrings.ODataSelectPath_OperationSegmentCanOnlyBeLastSegment);
                    }
                }
                else if (segment is TypeSegment)
                {
                    if (index == this.Count - 1)
                    {
                        throw new ODataException(ODataErrorStrings.ODataSelectPath_CannotEndInTypeSegment);
                    }
                }
                else if (segment is OpenPropertySegment || segment is PropertySegment)
                {
                    continue;
                }
                else
                {
                    throw new ODataException(ODataErrorStrings.ODataSelectPath_InvalidSelectPathSegmentType(segment.GetType().Name));
                }

                index++;
            }
        }
Пример #3
0
        /// <summary>
        /// Parses typed literals.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <param name="targetTypeReference">Expected type to be parsed.</param>
        /// <param name="targetTypeName">The EDM type name of the expected type to be parsed.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static LiteralToken ParseTypedLiteral(ExpressionLexer lexer, IEdmTypeReference targetTypeReference, string targetTypeName)
        {
            Debug.Assert(lexer != null, "lexer != null");

            UriLiteralParsingException typeParsingException;
            object targetValue = DefaultUriLiteralParser.Instance.ParseUriStringToType(lexer.CurrentToken.Text, targetTypeReference, out typeParsingException);

            if (targetValue == null)
            {
                string message;

                if (typeParsingException == null)
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteral(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText);

                    throw ParseError(message);
                }
                else
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteralWithReason(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText,
                        typeParsingException.Message);

                    throw ParseError(message, typeParsingException);
                }
            }

            LiteralToken result = new LiteralToken(targetValue, lexer.CurrentToken.Text);

            lexer.NextToken();
            return(result);
        }
Пример #4
0
        // parses $apply aggregate tranformation (.e.g. aggregate(UnitPrice with sum as TotalUnitPrice)
        internal AggregateToken ParseAggregate()
        {
            Debug.Assert(TokenIdentifierIs(ExpressionConstants.KeywordAggregate), "token identifier is aggregate");
            lexer.NextToken();

            // '('
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen)
            {
                throw ParseError(ODataErrorStrings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.lexer.NextToken();

            // series of statements separates by commas
            var statements = new List <AggregateExpressionToken>();

            while (true)
            {
                statements.Add(this.ParseAggregateExpression());

                if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Comma)
                {
                    break;
                }

                this.lexer.NextToken();
            }

            // ")"
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
            {
                throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.lexer.NextToken();

            return(new AggregateToken(statements));
        }
        /// <summary>
        /// Reads an annotation's value from the annotation value notation specified on the current element.
        /// </summary>
        /// <param name="expectedTypeReference">The expected type reference of the vocabulary term from the metadata.</param>
        /// <param name="attributeValueNotationTypeReference">The type reference indicated by the name of the attribute used in attribute value notation.
        ///   For example, if the attribute was called "string", this will be a reference to the string type.</param>
        /// <param name="attributeValueNotationAttributeName">The name of the attribute used by attribute avalue notation.</param>
        /// <param name="attributeValueNotationAttributeValue">The value of the attribute used by attribute value notation.</param>
        /// <param name="typeAttributeValue">The value of the "m:type" attribute on the annotation element.</param>
        /// <param name="positionedOnEmptyElement">true if the annotation element is empty, false otherwise.</param>
        /// <param name="model">The edm model instance.</param>
        /// <param name="messageReaderSettings">The message reader settings instance.</param>
        /// <param name="version">The payload version to read.</param>
        /// <returns>The primitive value represented on this element via attribute value notation.</returns>
        private static ODataPrimitiveValue GetValueFromAttributeValueNotation(
            IEdmTypeReference expectedTypeReference,
            IEdmPrimitiveTypeReference attributeValueNotationTypeReference,
            string attributeValueNotationAttributeName,
            string attributeValueNotationAttributeValue,
            string typeAttributeValue,
            bool positionedOnEmptyElement,
            IEdmModel model,
            ODataMessageReaderSettings messageReaderSettings,
            ODataVersion version)
        {
            Debug.Assert(attributeValueNotationTypeReference != null, "attributeValueNotationTypeReference != null");

            if (!positionedOnEmptyElement)
            {
                // If there is content in the body of the element, throw since it's ambiguous whether we should use the value from the attribute or the element content.
                throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_AttributeValueNotationUsedOnNonEmptyElement(attributeValueNotationAttributeName));
            }

            // If both m:type is present and attribute value notation is being used, they must match.
            // For example, if m:type is "Edm.Int32", but the "string" attribute is also present, we should throw.
            if (typeAttributeValue != null && !string.Equals(attributeValueNotationTypeReference.Definition.ODataFullName(), typeAttributeValue, StringComparison.Ordinal))
            {
                throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_AttributeValueNotationUsedWithIncompatibleType(typeAttributeValue, attributeValueNotationAttributeName));
            }

            IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolveAndValidatePrimitiveTargetType(
                expectedTypeReference,
                EdmTypeKind.Primitive,
                attributeValueNotationTypeReference.Definition,
                attributeValueNotationTypeReference.ODataFullName(),
                attributeValueNotationTypeReference.Definition,
                model,
                messageReaderSettings,
                version);

            return(new ODataPrimitiveValue(AtomValueUtils.ConvertStringToPrimitive(attributeValueNotationAttributeValue, targetTypeReference.AsPrimitive())));
        }
        /// <summary>
        /// Handles the start of primary expressions.
        /// </summary>
        /// <returns>The lexical token representing the expression.</returns>
        private QueryToken ParsePrimaryStart()
        {
            switch (this.lexer.CurrentToken.Kind)
            {
            case ExpressionTokenKind.ParameterAlias:
            {
                return(ParseParameterAlias(this.lexer));
            }

            case ExpressionTokenKind.Identifier:
            {
                IdentifierTokenizer identifierTokenizer = new IdentifierTokenizer(this.parameters, new FunctionCallParser(this.lexer, this));
                return(identifierTokenizer.ParseIdentifier(null));
            }

            case ExpressionTokenKind.OpenParen:
            {
                return(this.ParseParenExpression());
            }

            case ExpressionTokenKind.Star:
            {
                IdentifierTokenizer identifierTokenizer = new IdentifierTokenizer(this.parameters, new FunctionCallParser(this.lexer, this));
                return(identifierTokenizer.ParseStarMemberAccess(null));
            }

            default:
            {
                QueryToken primitiveLiteralToken = TryParseLiteral(this.lexer);
                if (primitiveLiteralToken == null)
                {
                    throw ParseError(ODataErrorStrings.UriQueryExpressionParser_ExpressionExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
                }

                return(primitiveLiteralToken);
            }
            }
        }
Пример #7
0
        public void InvalidDateReaderTest()
        {
            var testCases = new[]
            {
                new { Payload = "\"\"", Show = "" },
                new { Payload = "\"value\"", Show = "value" },
                new { Payload = "42", Show = "42" },
                new { Payload = "true", Show = "True" },
                new { Payload = "\"\\/Date(-0001-01-01)\\/\"", Show = "/Date(-0001-01-01)/" },
                new { Payload = "\"\\/Date(-9999-12-31)\\/\"", Show = "/Date(-9999-12-31)/" },
                new { Payload = "\"\\/Date(2012-04-13T02:43:10.215Z)\\/\"", Show = "/Date(2012-04-13T02:43:10.215Z)/" },
                new { Payload = "\"2/26/2011\"", Show = "2/26/2011" },
                new { Payload = "\"\\/Date(1298678400000)\\/\"", Show = "/Date(1298678400000)/" },
                new { Payload = "\"\\/Date(1286705410000+0060)\\/\"", Show = "/Date(1286705410000+0060)/" },
                new { Payload = "\"7-dui:9M7UG{*'!pu:^8LaV8a9~Pt76Fn*sP*1Tdf\"", Show = "7-dui:9M7UG{*'!pu:^8LaV8a9~Pt76Fn*sP*1Tdf" },
            };

            foreach (var testCase in testCases)
            {
                Action action = () => this.VerifyDateValueReader(testCase.Payload, "Edm.Date", null);
                action.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ReaderValidationUtils_CannotConvertPrimitiveValue(testCase.Show, "Edm.Date"));
            }
        }
Пример #8
0
        /// <summary>
        /// Read a query option from the lexer.
        /// </summary>
        /// <returns>The query option as a string.</returns>
        private string ReadQueryOption()
        {
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Equal)
            {
                throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
            }

            // get the full text from the current location onward
            // there could be literals like 'A string literal; tricky!' in there, so we need to be careful.
            // Also there could be more nested (...) expressions that we ignore until we recurse enough times to get there.
            string expressionText = this.lexer.AdvanceThroughExpandOption();

            if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.SemiColon)
            {
                // Move over the ';' seperator
                this.lexer.NextToken();
                return(expressionText);
            }

            // If there wasn't a semicolon, it MUST be the last option. We must be at ')' in this case
            this.lexer.ValidateToken(ExpressionTokenKind.CloseParen);
            return(expressionText);
        }
Пример #9
0
        public void AnnotationOnEntryTargetingSomethingOtherThanEntryShouldThrow()
        {
            const string payload = @"
                <entry xmlns=""http://www.w3.org/2005/Atom"" xmlns:d=""http://docs.oasis-open.org/odata/ns/data"" xmlns:m=""http://docs.oasis-open.org/odata/ns/metadata"">
                    <id/>
                    <title/>
                    <m:annotation term=""my.namespace.term"" target=""PropertyName"" m:type=""Edm.Int32"">42</m:annotation>
                    <updated>2013-01-22T01:09:20Z</updated>
                    <author>
                        <name/>
                    </author>
                    <content type=""application/xml"">
                    <m:properties>
                        <d:PropertyName>PropertyValue</d:PropertyName>
                    </m:properties>
                    </content>
                </entry>
                ";

            Action testSubject = () => this.ReadEntryPayload(payload, reader => { });

            testSubject.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.ODataAtomEntryAndFeedDeserializer_AnnotationWithNonDotTarget("PropertyName", "my.namespace.term"));
        }
Пример #10
0
        /// <summary>
        /// Resolves a type.
        /// </summary>
        /// <param name="typeName">The type name.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <returns>The resolved Edm type.</returns>
        private ODataPayloadKind ResolveType(string typeName, ODataReaderBehavior readerBehavior)
        {
            string typeNameToResolve = EdmLibraryExtensions.GetCollectionItemTypeName(typeName) ?? typeName;
            bool   isCollection      = typeNameToResolve != typeName;

            EdmTypeKind typeKind;
            IEdmType    resolvedType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeNameToResolve, readerBehavior, out typeKind);

            if (resolvedType == null || resolvedType.TypeKind != EdmTypeKind.Primitive && resolvedType.TypeKind != EdmTypeKind.Enum && resolvedType.TypeKind != EdmTypeKind.Complex && resolvedType.TypeKind != EdmTypeKind.Entity && resolvedType.TypeKind != EdmTypeKind.TypeDefinition)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidEntitySetNameOrTypeName(UriUtils.UriToString(this.parseResult.ContextUri), typeName));
            }

            if (resolvedType.TypeKind == EdmTypeKind.Entity)
            {
                this.parseResult.EdmType = resolvedType;
                return(isCollection ? ODataPayloadKind.Feed : ODataPayloadKind.Entry);
            }

            resolvedType             = isCollection ? EdmLibraryExtensions.GetCollectionType(resolvedType.ToTypeReference(true /*nullable*/)) : resolvedType;
            this.parseResult.EdmType = resolvedType;
            return(isCollection ? ODataPayloadKind.Collection : ODataPayloadKind.Property);
        }
        /// <summary>
        /// Reads an item in the collection.
        /// </summary>
        /// <param name="expectedItemType">The expected type of the item to read.</param>
        /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param>
        /// <returns>The value of the collection item that was read; this can be an ODataComplexValue, a primitive value or 'null'.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element    - The start element node of the item in the collection.
        /// Post-Condition: Any                    - The next node after the end tag of the item.
        /// </remarks>
        internal object ReadCollectionItem(IEdmTypeReference expectedItemType, CollectionWithoutExpectedTypeValidator collectionValidator)
        {
            this.XmlReader.AssertNotBuffering();
            this.AssertXmlCondition(XmlNodeType.Element);

            // the caller should guarantee that we are reading elements in the OData namespace or the custom namespace specified through the reader settings.
            Debug.Assert(this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace), "The 'element' node should be in the OData Metadata Namespace or in the user specified Metadata Namespace");

            // make sure that the item is named as 'element'.
            if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
            {
                throw new ODataException(ODataErrorStrings.ODataAtomCollectionDeserializer_WrongCollectionItemElementName(this.XmlReader.LocalName, this.XmlReader.ODataNamespace));
            }

            object item = this.ReadNonEntityValue(expectedItemType, this.duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true);

            // read over the end tag of the element or the start tag if the element was empty.
            this.XmlReader.Read();

            this.XmlReader.AssertNotBuffering();

            return(item);
        }
Пример #12
0
        /// <summary>
        /// Parses typed literals.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <param name="targetTypeReference">Expected type to be parsed.</param>
        /// <param name="targetTypeName">The EDM type name of the expected type to be parsed.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static LiteralToken ParseTypedLiteral(ExpressionLexer lexer, IEdmPrimitiveTypeReference targetTypeReference, string targetTypeName)
        {
            Debug.Assert(lexer != null, "lexer != null");

            object targetValue;
            string reason;

            if (!UriPrimitiveTypeParser.TryUriStringToPrimitive(lexer.CurrentToken.Text, targetTypeReference, out targetValue, out reason))
            {
                string message;

                if (reason == null)
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteral(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText);
                }
                else
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteralWithReason(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText,
                        reason);
                }

                throw ParseError(message);
            }

            LiteralToken result = new LiteralToken(targetValue, lexer.CurrentToken.Text);

            lexer.NextToken();
            return(result);
        }
Пример #13
0
        /// <summary>
        /// Writes a primitive value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the primitive value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this primitive value. The annotation value will override whatever type name is being written.</param>
        internal void WritePrimitiveValue(
            object value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            Debug.Assert(value != null, "value != null");

            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType());

            if (primitiveTypeReference == null)
            {
                throw new ODataException(ODataErrorStrings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName));
            }

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive);
            }

            if (expectedTypeReference != null)
            {
                ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference);
            }

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, primitiveTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);

            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            if (typeName != null && typeName != EdmConstants.EdmStringTypeName)
            {
                this.WritePropertyTypeAttribute(typeName);
            }

            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value);
        }
        /// <summary>
        /// Reads the start element of a collection.
        /// </summary>
        /// <param name="isCollectionElementEmpty">true, if the collection element is empty; false otherwise.</param>
        /// <returns>An <see cref="ODataCollectionStart"/> representing the collection-level information. Currently this only contains
        /// the name of the collection.</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element - The start element of the collection.
        /// Post-Condition:  Any                 - The next node after the start element node of the collection or the
        ///                                        empty collection element node.
        /// </remarks>
        internal ODataCollectionStart ReadCollectionStart(out bool isCollectionElementEmpty)
        {
            this.XmlReader.AssertNotBuffering();
            this.AssertXmlCondition(XmlNodeType.Element);

            if (!this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace))
            {
                throw new ODataException(ODataErrorStrings.ODataAtomCollectionDeserializer_TopLevelCollectionElementWrongNamespace(this.XmlReader.NamespaceURI, this.XmlReader.ODataMetadataNamespace));
            }

            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace) &&
                    (this.XmlReader.LocalNameEquals(this.AtomTypeAttributeName) ||
                     (this.XmlReader.LocalNameEquals(this.ODataNullAttributeName))))
                {
                    // make sure that m:type or m:null attributes are not present in the root element of the collection.
                    throw new ODataException(ODataErrorStrings.ODataAtomCollectionDeserializer_TypeOrNullAttributeNotAllowed);
                }
            }

            // ignore all other attributes.
            this.XmlReader.MoveToElement();

            ODataCollectionStart collectionStart = new ODataCollectionStart();

            isCollectionElementEmpty = this.XmlReader.IsEmptyElement;

            if (!isCollectionElementEmpty)
            {
                // if the collection start element is not an empty element than read over the
                // start element.
                this.XmlReader.Read();
            }

            return(collectionStart);
        }
        /// <summary>
        /// Build a segment from a token.
        /// </summary>
        /// <param name="tokenIn">the token to bind</param>
        /// <param name="model">The model.</param>
        /// <param name="edmType">the type of the current scope based on type segments.</param>
        /// <param name="resolver">Resolver for uri parser.</param>
        /// <returns>The segment created from the token.</returns>
        public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver = null)
        {
            if (resolver == null)
            {
                resolver = ODataUriResolver.Default;
            }

            ODataPathSegment nextSegment;

            if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment))
            {
                return(nextSegment);
            }

            // Operations must be container-qualified, and because the token type indicates it was not a .-seperated identifier, we should not try to look up operations.
            if (tokenIn.IsNamespaceOrContainerQualified())
            {
                if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment))
                {
                    return(nextSegment);
                }

                // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName
                // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem.
                if (!edmType.IsOpen)
                {
                    return(null);
                }
            }

            if (edmType.IsOpen)
            {
                return(new OpenPropertySegment(tokenIn.Identifier));
            }

            throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.ODataFullName(), tokenIn.Identifier));
        }
Пример #16
0
        /// <summary>
        /// Binds key values to a key lookup on a collection.
        /// </summary>
        /// <param name="collectionNode">Already bound collection node.</param>
        /// <param name="namedValues">The named value tokens to bind.</param>
        /// <param name="model">The model to be used.</param>
        /// <returns>The bound key lookup.</returns>
        internal QueryNode BindKeyValues(EntityCollectionNode collectionNode, IEnumerable <NamedValue> namedValues, IEdmModel model)
        {
            Debug.Assert(namedValues != null, "namedValues != null");
            Debug.Assert(collectionNode != null, "CollectionNode != null");
            Debug.Assert(model != null, "model != null");

            IEdmEntityTypeReference collectionItemType = collectionNode.EntityItemType;

            IEdmEntityType collectionItemEntityType = collectionItemType.EntityDefinition();
            QueryNode      keyLookupNode;

            if (TryBindToDeclaredKey(collectionNode, namedValues, model, collectionItemEntityType, out keyLookupNode))
            {
                return(keyLookupNode);
            }
            else if (TryBindToDeclaredAlternateKey(collectionNode, namedValues, model, collectionItemEntityType, out keyLookupNode))
            {
                return(keyLookupNode);
            }
            else
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_NotAllKeyPropertiesSpecifiedInKeyValues(collectionNode.ItemType.FullName()));
            }
        }
Пример #17
0
        /// <summary>
        /// Parses the primary expressions.
        /// </summary>
        /// <returns>The lexical token representing the expression.</returns>
        private QueryToken ParsePrimary()
        {
            QueryToken expr = null;

            this.RecurseEnter();

            switch (this.lexer.CurrentToken.Kind)
            {
            case ExpressionTokenKind.OpenParen:
                expr = this.ParseParenExpression();
                break;

            case ExpressionTokenKind.StringLiteral:
                expr = new StringLiteralToken(this.lexer.CurrentToken.Text);
                this.lexer.NextToken();
                break;

            default:
                throw new ODataException(ODataErrorStrings.UriQueryExpressionParser_ExpressionExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.RecurseLeave();
            return(expr);
        }
Пример #18
0
        /// <summary>
        /// Compute the result type of a binary operator based on the type of its operands and the operator kind.
        /// </summary>
        /// <param name="left">The type reference on the left hand.</param>
        /// <param name="right">The type reference on the right hand.</param>
        /// <param name="operatorKind">The kind of operator.</param>
        /// <returns>The result type reference of the binary operator.</returns>
        internal static IEdmPrimitiveTypeReference GetBinaryOperatorResultType(IEdmPrimitiveTypeReference left, IEdmPrimitiveTypeReference right, BinaryOperatorKind operatorKind)
        {
            Debug.Assert(left != null, "type != null");
            Debug.Assert(right != null, "type != null");

            EdmPrimitiveTypeKind kind;

            if (additionalMap.TryGetValue(new Tuple <BinaryOperatorKind, EdmPrimitiveTypeKind, EdmPrimitiveTypeKind>(operatorKind, left.PrimitiveKind(), right.PrimitiveKind()), out kind))
            {
                return(EdmCoreModel.Instance.GetPrimitive(kind, left.IsNullable));
            }

            switch (operatorKind)
            {
            case BinaryOperatorKind.Or:                     // fall through
            case BinaryOperatorKind.And:                    // fall through
            case BinaryOperatorKind.Equal:                  // fall through
            case BinaryOperatorKind.NotEqual:               // fall through
            case BinaryOperatorKind.GreaterThan:            // fall through
            case BinaryOperatorKind.GreaterThanOrEqual:     // fall through
            case BinaryOperatorKind.LessThan:               // fall through
            case BinaryOperatorKind.LessThanOrEqual:
            case BinaryOperatorKind.Has:
                return(EdmCoreModel.Instance.GetBoolean(left.IsNullable));

            case BinaryOperatorKind.Add:            // fall through
            case BinaryOperatorKind.Subtract:       // fall through
            case BinaryOperatorKind.Multiply:       // fall through
            case BinaryOperatorKind.Divide:         // fall through
            case BinaryOperatorKind.Modulo:
                return(left);

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.QueryNodeUtils_BinaryOperatorResultType_UnreachableCodepath));
            }
        }
        private static bool TryBindAsDeclaredProperty(PathSegmentToken tokenIn, IEdmStructuredType edmType, ODataUriResolver resolver, out ODataPathSegment segment)
        {
            IEdmProperty prop = resolver.ResolveProperty(edmType, tokenIn.Identifier);

            if (prop == null)
            {
                segment = null;
                return(false);
            }

            if (prop.PropertyKind == EdmPropertyKind.Structural)
            {
                segment = new PropertySegment((IEdmStructuralProperty)prop);
                return(true);
            }

            if (prop.PropertyKind == EdmPropertyKind.Navigation)
            {
                segment = new NavigationPropertySegment((IEdmNavigationProperty)prop, null /*TODO set*/);
                return(true);
            }

            throw new ODataException(ODataErrorStrings.SelectExpandBinder_UnknownPropertyType(prop.Name));
        }
        /// <summary>
        /// Verifies that CreateEntryReader or CreateFeedReader or CreateDeltaReader can be called.
        /// </summary>
        /// <param name="navigationSource">The navigation source we are going to read entities for.</param>
        /// <param name="entityType">The expected entity type for the entry/entries to be read.</param>
        private void VerifyCanCreateODataReader(IEdmNavigationSource navigationSource, IEdmEntityType entityType)
        {
            Debug.Assert(navigationSource == null || entityType != null, "If an navigation source is specified, the entity type must be specified as well.");

            // We require metadata information for reading requests.
            if (!this.ReadingResponse)
            {
                this.VerifyUserModel();

                if (navigationSource == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightInputContext_NoEntitySetForRequest);
                }
            }

            // We only check that the base type of the entity set is assignable from the specified entity type.
            // If no entity set/entity type is specified in the API, we will read it from the context URI.
            IEdmEntityType entitySetElementType = this.EdmTypeResolver.GetElementType(navigationSource);

            if (navigationSource != null && entityType != null && !entityType.IsOrInheritsFrom(entitySetElementType))
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightInputContext_EntityTypeMustBeCompatibleWithEntitySetBaseType(entityType.FullName(), entitySetElementType.FullName(), navigationSource.FullNavigationSourceName()));
            }
        }
        public BatchReferenceSegment(string contentId, IEdmType edmType, IEdmEntitySetBase entitySet)
        {
            ExceptionUtils.CheckArgumentNotNull(edmType, "resultingType");
            ExceptionUtils.CheckArgumentNotNull(contentId, "contentId");
            if (!ODataPathParser.ContentIdRegex.IsMatch(contentId))
            {
                throw new ODataException(ODataErrorStrings.BatchReferenceSegment_InvalidContentID(contentId));
            }

            this.edmType   = edmType;
            this.entitySet = entitySet;
            this.contentId = contentId;

            this.Identifier                = this.ContentId;
            this.TargetEdmType             = edmType;
            this.TargetEdmNavigationSource = this.EntitySet;
            this.SingleResult              = true;
            this.TargetKind                = RequestTargetKind.Resource;

            if (entitySet != null)
            {
                UriParserErrorHelper.ThrowIfTypesUnrelated(edmType, entitySet.EntityType(), "BatchReferenceSegments");
            }
        }
Пример #22
0
        /// <summary>
        /// Read an entity reference link.
        /// </summary>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to check for duplicate properties and
        /// duplicate annotations; this is a separate instance per entity reference link.</param>
        /// <param name="topLevel">true if we are reading a singleton entity reference link at the top level; false if we are reading
        /// an entity reference link as part of a collection of entity reference links.</param>
        /// <returns>An instance of <see cref="ODataEntityReferenceLink"/> which was read.</returns>
        /// <remarks>
        /// Pre-Condition:  StartObject     when the entity reference link is part of a collection
        ///                 Property        the first property in the entity reference link (for a top-level link)
        ///                 EndObject       the end object node of an entity reference link (for a top-level link)
        /// Post-Condition: EndInput        for a top-level object
        ///                 EndArray        for the last link in a collection of links
        ///                 Any             for the first node of the next link in a collection of links
        /// </remarks>
        private ODataEntityReferenceLink ReadSingleEntityReferenceLink(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool topLevel)
        {
            this.JsonReader.AssertNotBuffering();

            if (!topLevel)
            {
                if (this.JsonReader.NodeType != JsonNodeType.StartObject)
                {
                    // entity reference link has to be an object
                    throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkMustBeObjectValue(this.JsonReader.NodeType));
                }

                this.JsonReader.ReadStartObject();
            }

            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);

            ODataEntityReferenceLink[] entityReferenceLink = { null };

            // Entity  reference links use instance annotations. Fail if we find a  property annotation.
            Func <string, object> propertyAnnotationValueReader =
                annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLink(annotationName)); };

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                this.ProcessProperty(
                    duplicatePropertyNamesChecker,
                    propertyAnnotationValueReader,
                    (propertyParsingResult, propertyName) =>
                {
                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        if (string.CompareOrdinal(ODataAnnotationNames.ODataId, propertyName) != 0)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyInEntityReferenceLink(propertyName, ODataAnnotationNames.ODataId));
                        }
                        else if (entityReferenceLink[0] != null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MultipleUriPropertiesInEntityReferenceLink(ODataAnnotationNames.ODataId));
                        }

                        // read the value of the 'odata.id' annotation
                        string urlString = this.JsonReader.ReadStringValue(ODataAnnotationNames.ODataId);
                        if (urlString == null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkUrlCannotBeNull(ODataAnnotationNames.ODataId));
                        }

                        entityReferenceLink[0] = new ODataEntityReferenceLink
                        {
                            Url = this.ProcessUriFromPayload(urlString)
                        };

                        ReaderValidationUtils.ValidateEntityReferenceLink(entityReferenceLink[0]);

                        break;

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        this.JsonReader.SkipValue();
                        break;

                    case PropertyParsingResult.PropertyWithValue:
                    case PropertyParsingResult.PropertyWithoutValue:
                        // entity reference link  is denoted by odata.id annotation
                        throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidAnnotationInEntityReferenceLink(propertyName));

                    case PropertyParsingResult.EndOfObject:
                        break;

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));

                    default:
                        throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadSingleEntityReferenceLink));
                    }
                });
            }

            if (entityReferenceLink[0] == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MissingEntityReferenceLinkProperty(ODataAnnotationNames.ODataId));
            }

            // end of the entity reference link object
            this.JsonReader.ReadEndObject();

            this.JsonReader.AssertNotBuffering();
            return(entityReferenceLink[0]);
        }
Пример #23
0
        // parses $apply groupby tranformation (.e.g. groupby(ProductID, CategoryId, aggregate(UnitPrice with sum as TotalUnitPrice))
        internal GroupByToken ParseGroupBy()
        {
            Debug.Assert(TokenIdentifierIs(ExpressionConstants.KeywordGroupBy), "token identifier is groupby");
            lexer.NextToken();

            // '('
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen)
            {
                throw ParseError(ODataErrorStrings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.lexer.NextToken();

            // '('
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen)
            {
                throw ParseError(ODataErrorStrings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.lexer.NextToken();

            // properties
            var properties = new List <EndPathToken>();

            while (true)
            {
                var expression = this.ParsePrimary() as EndPathToken;

                if (expression == null)
                {
                    throw ParseError(ODataErrorStrings.UriQueryExpressionParser_ExpressionExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
                }

                properties.Add(expression);

                if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Comma)
                {
                    break;
                }

                this.lexer.NextToken();
            }

            // ")"
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
            {
                throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrOperatorExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.lexer.NextToken();

            // optional child transformation
            ApplyTransformationToken transformationToken = null;

            // "," (comma)
            if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Comma)
            {
                this.lexer.NextToken();

                if (TokenIdentifierIs(ExpressionConstants.KeywordAggregate))
                {
                    transformationToken = this.ParseAggregate();
                }
                else
                {
                    throw ParseError(ODataErrorStrings.UriQueryExpressionParser_KeywordOrIdentifierExpected(ExpressionConstants.KeywordAggregate, this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
                }
            }

            // ")"
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
            {
                throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
            }

            this.lexer.NextToken();

            return(new GroupByToken(properties, transformationToken));
        }
Пример #24
0
        public void GetOrCreateReadOnlyEnumerableShouldThrowForListSource()
        {
            Action test = () => (new List <int>()).GetOrCreateReadOnlyEnumerable("Integers");

            test.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ReaderUtils_EnumerableModified("Integers"));
        }
Пример #25
0
        public void ConcatToReadOnlyEnumerableShouldThrowForListSource()
        {
            Action test = () => new List <ODataProperty>().ConcatToReadOnlyEnumerable("Properties", new ODataProperty());

            test.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ReaderUtils_EnumerableModified("Properties"));
        }
Пример #26
0
        /// <summary>
        /// Binds a <see cref="InnerPathToken"/>.
        /// This includes more than just navigations - it includes complex property access and primitive collections.
        /// </summary>
        /// <param name="segmentToken">The segment token to bind.</param>
        /// <returns>The bound node.</returns>
        internal QueryNode BindInnerPathSegment(InnerPathToken segmentToken)
        {
            FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.bindMethod, state);

            // First we get the parent node
            QueryNode parent = this.DetermineParentNode(segmentToken, state);

            Debug.Assert(parent != null, "parent should never be null");

            SingleValueNode singleValueParent = parent as SingleValueNode;

            if (singleValueParent == null)
            {
                QueryNode boundFunction;
                if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction))
                {
                    return(boundFunction);
                }

                throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(segmentToken.Identifier));
            }

            // Using the parent and name of this token, we try to get the IEdmProperty it represents
            IEdmProperty property = BindProperty(singleValueParent.TypeReference, segmentToken.Identifier, this.Resolver);

            if (property == null)
            {
                QueryNode boundFunction;
                if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction))
                {
                    return(boundFunction);
                }

                if (singleValueParent.TypeReference != null && !singleValueParent.TypeReference.Definition.IsOpenType())
                {
                    throw new ODataException(
                              ODataErrorStrings.MetadataBinder_PropertyNotDeclared(
                                  parent.GetEdmTypeReference().FullName(), segmentToken.Identifier));
                }

                return(new SingleValueOpenPropertyAccessNode(singleValueParent, segmentToken.Identifier));
            }

            if (property.Type.IsODataComplexTypeKind())
            {
                return(new SingleValuePropertyAccessNode(singleValueParent, property));
            }

            // Note - this means nonentity collection (primitive or complex)
            if (property.Type.IsNonEntityCollectionType())
            {
                return(new CollectionPropertyAccessNode(singleValueParent, property));
            }

            IEdmNavigationProperty navigationProperty = property as IEdmNavigationProperty;

            if (navigationProperty == null)
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_IllegalSegmentType(property.Name));
            }

            SingleEntityNode parentEntity = EnsureParentIsEntityForNavProp(singleValueParent);

            return(GetNavigationNode(navigationProperty, parentEntity, segmentToken.NamedValues, state, new KeyBinder(this.bindMethod)));
        }
        internal static object ConvertValue(
            object value,
            IEdmPrimitiveTypeReference primitiveTypeReference,
            ODataMessageReaderSettings messageReaderSettings,
            ODataVersion version,
            bool validateNullValue,
            string propertyName)
        {
            Debug.Assert(primitiveTypeReference != null, "primitiveTypeReference != null");

            if (value == null)
            {
                // Only primitive type references are validated. Core model is sufficient.
                ReaderValidationUtils.ValidateNullValue(
                    EdmCoreModel.Instance,
                    primitiveTypeReference,
                    messageReaderSettings,
                    validateNullValue,
                    version,
                    propertyName);
                return(null);
            }

            try
            {
                Type targetType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false);

                string stringValue = value as string;
                if (stringValue != null)
                {
                    return(ConvertStringValue(stringValue, targetType));
                }
                else if (value is Int32)
                {
                    return(ConvertInt32Value((int)value, targetType, primitiveTypeReference));
                }
                else if (value is Decimal)
                {
                    Decimal decimalValue = (Decimal)value;
                    if (targetType == typeof(Int64))
                    {
                        return(Convert.ToInt64(decimalValue));
                    }

                    if (targetType == typeof(Double))
                    {
                        return(Convert.ToDouble(decimalValue));
                    }

                    if (targetType == typeof(Single))
                    {
                        return(Convert.ToSingle(decimalValue));
                    }

                    if (targetType != typeof(Decimal))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDecimal(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is Double)
                {
                    Double doubleValue = (Double)value;
                    if (targetType == typeof(Single))
                    {
                        return(Convert.ToSingle(doubleValue));
                    }

                    if (targetType != typeof(Double))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is bool)
                {
                    if (targetType != typeof(bool))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is DateTime)
                {
                    if (targetType != typeof(DateTime))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is DateTimeOffset)
                {
                    if (targetType != typeof(DateTimeOffset))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.ODataFullName()));
                    }
                }
            }
            catch (Exception e)
            {
                if (!ExceptionUtils.IsCatchableExceptionType(e))
                {
                    throw;
                }

                throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, e, value.ToString());
            }

            // otherwise just return the value without doing any conversion
            return(value);
        }
Пример #28
0
        /// <summary>
        /// Generate an expand item (and a select item for the implicit nav prop if necessary) based on an ExpandTermToken
        /// </summary>
        /// <param name="tokenIn">the expandTerm token to visit</param>
        /// <returns>the expand item for this expand term token.</returns>
        private SelectItem GenerateExpandItem(ExpandTermToken tokenIn)
        {
            ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn");

            // ensure that we're always dealing with proper V4 syntax
            if (tokenIn.PathToNavProp.NextToken != null && !tokenIn.PathToNavProp.IsNamespaceOrContainerQualified())
            {
                if (tokenIn.PathToNavProp.NextToken.Identifier != UriQueryConstants.RefSegment || tokenIn.PathToNavProp.NextToken.NextToken != null)
                {
                    throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath);
                }
            }

            PathSegmentToken currentToken = tokenIn.PathToNavProp;

            IEdmStructuredType      currentLevelEntityType = this.EdmType;
            List <ODataPathSegment> pathSoFar         = new List <ODataPathSegment>();
            PathSegmentToken        firstNonTypeToken = currentToken;

            if (currentToken.IsNamespaceOrContainerQualified())
            {
                pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(currentToken, this.Model, this.Settings.SelectExpandLimit, this.configuration.Resolver, ref currentLevelEntityType, out firstNonTypeToken));
            }

            IEdmProperty edmProperty = this.configuration.Resolver.ResolveProperty(currentLevelEntityType, firstNonTypeToken.Identifier);

            if (edmProperty == null)
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(currentLevelEntityType.FullTypeName(), currentToken.Identifier));
            }

            IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty;

            if (currentNavProp == null)
            {
                throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationProperty(currentToken.Identifier, currentLevelEntityType.FullTypeName()));
            }

            bool isRef = false;

            if (firstNonTypeToken.NextToken != null)
            {
                // lastly... make sure that, since we're on a NavProp, that the next token isn't null.
                if (firstNonTypeToken.NextToken.Identifier == UriQueryConstants.RefSegment)
                {
                    isRef = true;
                }
                else
                {
                    throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath);
                }
            }

            pathSoFar.Add(new NavigationPropertySegment(currentNavProp, /*entitySet*/ null));
            ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar);

            IEdmNavigationSource targetNavigationSource = null;

            if (this.NavigationSource != null)
            {
                targetNavigationSource = this.NavigationSource.FindNavigationTarget(currentNavProp);
            }

            // call MetadataBinder to build the filter clause
            FilterClause filterOption = null;

            if (tokenIn.FilterOption != null)
            {
                MetadataBinder binder       = this.BuildNewMetadataBinder(targetNavigationSource);
                FilterBinder   filterBinder = new FilterBinder(binder.Bind, binder.BindingState);
                filterOption = filterBinder.BindFilter(tokenIn.FilterOption);
            }

            // call MetadataBinder again to build the orderby clause
            OrderByClause orderbyOption = null;

            if (tokenIn.OrderByOptions != null)
            {
                MetadataBinder binder        = this.BuildNewMetadataBinder(targetNavigationSource);
                OrderByBinder  orderByBinder = new OrderByBinder(binder.Bind);
                orderbyOption = orderByBinder.BindOrderBy(binder.BindingState, tokenIn.OrderByOptions);
            }

            SearchClause searchOption = null;

            if (tokenIn.SearchOption != null)
            {
                MetadataBinder binder       = this.BuildNewMetadataBinder(targetNavigationSource);
                SearchBinder   searchBinder = new SearchBinder(binder.Bind);
                searchOption = searchBinder.BindSearch(tokenIn.SearchOption);
            }

            if (isRef)
            {
                return(new ExpandedReferenceSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption));
            }

            SelectExpandClause subSelectExpand;

            if (tokenIn.ExpandOption != null)
            {
                subSelectExpand = this.GenerateSubExpand(currentNavProp, tokenIn);
            }
            else
            {
                subSelectExpand = BuildDefaultSubExpand();
            }

            subSelectExpand = this.DecorateExpandWithSelect(subSelectExpand, currentNavProp, tokenIn.SelectOption);

            LevelsClause levelsOption = this.ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp);

            return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, subSelectExpand, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, levelsOption));
        }
        public void DeltaLinkComesAfterInnerFeedShouldThrow()
        {
            foreach (bool isResponse in new[] { true, false })
            {
                const string entryText = @"
                ""NavProp"" : [],
                ""*****@*****.**"" : ""http://deltaLink""";

                var entryReader = GetEntryReader(entryText, isResponse);
                entryReader.Read();
                entryReader.State.Should().Be(ODataReaderState.EntryStart);
                entryReader.Read();
                entryReader.State.Should().Be(ODataReaderState.NavigationLinkStart);
                entryReader.Read();
                entryReader.State.Should().Be(ODataReaderState.FeedStart);
                entryReader.Item.As <ODataFeed>().NextPageLink.Should().Be(null);
                Action test             = () => entryReader.Read();
                string expectedErrorMsg = isResponse ? ErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedPropertyAnnotationAfterExpandedFeed("odata.deltaLink", "NavProp") : ErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedPropertyAnnotation("NavProp", "odata.deltaLink");
                test.ShouldThrow <ODataException>().WithMessage(expectedErrorMsg);
            }
        }
        internal static bool TryUriStringToPrimitive(string text, IEdmTypeReference targetType, out object targetValue, out string reason)
        {
            Debug.Assert(text != null, "text != null");
            Debug.Assert(targetType != null, "targetType != null");

            reason = null;

            if (targetType.IsNullable)
            {
                if (text == ExpressionConstants.KeywordNull)
                {
                    targetValue = null;
                    return(true);
                }
            }

            IEdmPrimitiveTypeReference primitiveTargetType = targetType.AsPrimitiveOrNull();

            if (primitiveTargetType == null)
            {
                targetValue = null;
                return(false);
            }

            EdmPrimitiveTypeKind targetTypeKind = primitiveTargetType.PrimitiveKind();

            byte[] byteArrayValue;
            bool   binaryResult = TryUriStringToByteArray(text, out byteArrayValue);

            if (targetTypeKind == EdmPrimitiveTypeKind.Binary)
            {
                targetValue = (object)byteArrayValue;
                return(binaryResult);
            }
            else if (binaryResult)
            {
                string keyValue = Encoding.UTF8.GetString(byteArrayValue, 0, byteArrayValue.Length);
                return(TryUriStringToPrimitive(keyValue, targetType, out targetValue));
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Guid)
            {
                Guid guidValue;
                bool result = UriUtils.TryUriStringToGuid(text, out guidValue);
                targetValue = guidValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.DateTimeOffset)
            {
                DateTimeOffset dateTimeOffsetValue;
                bool           result = UriUtils.TryUriStringToDateTimeOffset(text, out dateTimeOffsetValue);
                targetValue = dateTimeOffsetValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Duration)
            {
                TimeSpan timespanValue;
                bool     result = TryUriStringToDuration(text, out timespanValue);
                targetValue = timespanValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Geography)
            {
                Geography geographyValue;
                bool      result = TryUriStringToGeography(text, out geographyValue, out reason);
                targetValue = geographyValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Geometry)
            {
                Geometry geometryValue;
                bool     result = TryUriStringToGeometry(text, out geometryValue, out reason);
                targetValue = geometryValue;
                return(result);
            }

            bool quoted = targetTypeKind == EdmPrimitiveTypeKind.String;

            if (quoted != IsUriValueQuoted(text))
            {
                targetValue = null;
                return(false);
            }

            if (quoted)
            {
                text = RemoveQuotes(text);
            }

            try
            {
                switch (targetTypeKind)
                {
                case EdmPrimitiveTypeKind.String:
                    targetValue = text;
                    break;

                case EdmPrimitiveTypeKind.Boolean:
                    targetValue = XmlConvert.ToBoolean(text);
                    break;

                case EdmPrimitiveTypeKind.Byte:
                    targetValue = XmlConvert.ToByte(text);
                    break;

                case EdmPrimitiveTypeKind.SByte:
                    targetValue = XmlConvert.ToSByte(text);
                    break;

                case EdmPrimitiveTypeKind.Int16:
                    targetValue = XmlConvert.ToInt16(text);
                    break;

                case EdmPrimitiveTypeKind.Int32:
                    targetValue = XmlConvert.ToInt32(text);
                    break;

                case EdmPrimitiveTypeKind.Int64:
                    TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixInt64, ref text);
                    targetValue = XmlConvert.ToInt64(text);
                    break;

                case EdmPrimitiveTypeKind.Single:
                    TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixSingle, ref text);
                    targetValue = XmlConvert.ToSingle(text);
                    break;

                case EdmPrimitiveTypeKind.Double:
                    TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDouble, ref text);
                    targetValue = XmlConvert.ToDouble(text);
                    break;

                case EdmPrimitiveTypeKind.Decimal:
                    TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDecimal, ref text);
                    try
                    {
                        targetValue = XmlConvert.ToDecimal(text);
                    }
                    catch (FormatException)
                    {
                        // we need to support exponential format for decimals since we used to support them in V1
                        decimal result;
                        if (Decimal.TryParse(text, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out result))
                        {
                            targetValue = result;
                        }
                        else
                        {
                            targetValue = default(Decimal);
                            return(false);
                        }
                    }

                    break;

                default:
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_TryUriStringToPrimitive));
                }

                return(true);
            }
            catch (FormatException)
            {
                targetValue = null;
                return(false);
            }
            catch (OverflowException)
            {
                targetValue = null;
                return(false);
            }
        }