/// <summary> /// Resolves an entity set with an optional type cast and updates the parse result. /// </summary> /// <param name="entitySet">The entity set to resolve the type cast against.</param> /// <param name="typeCast">The optional type cast.</param> /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param> /// <param name="version">The version of the payload being read.</param> /// <param name="entitySetElementType">The type of the given entity set.</param> /// <returns>The resolved entity type.</returns> private IEdmEntityType ResolveTypeCast(IEdmEntitySet entitySet, string typeCast, ODataReaderBehavior readerBehavior, ODataVersion version, IEdmEntityType entitySetElementType) { Debug.Assert(entitySet != null, "entitySet != null"); IEdmEntityType entityType = entitySetElementType; // Parse the type cast if it exists if (!string.IsNullOrEmpty(typeCast)) { EdmTypeKind typeKind; entityType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeCast, readerBehavior, version, out typeKind) as IEdmEntityType; if (entityType == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidEntityTypeInTypeCast(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), typeCast)); } // Validate that the entity type is assignable to the base type of the set if (!entitySetElementType.IsAssignableFrom(entityType)) { throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_IncompatibleEntityTypeInTypeCast(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), typeCast, entitySetElementType.FullName(), entitySet.FullName())); } } return(entityType); }
/// <summary> /// Verifies that CreateParameterReader can be called. /// </summary> /// <param name="functionImport">The function import whose parameters are being read.</param> private static void VerifyCanCreateParameterReader(IEdmFunctionImport functionImport) { if (functionImport == null) { throw new ArgumentNullException("functionImport", ODataErrorStrings.ODataJsonInputContext_FunctionImportCannotBeNullForCreateParameterReader("functionImport")); } }
/// <summary> /// Implementation of the collection reader logic when in state 'Start'. /// </summary> /// <returns>true if more items can be read from the reader; otherwise false.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None: assumes that the JSON reader has not been used yet when not reading a nested payload. /// Post-Condition: The reader is positioned on the first node of the first item or the EndArray node of an empty item array /// </remarks> protected override bool ReadAtStartImplementation() { Debug.Assert(this.State == ODataCollectionReaderState.Start, "this.State == ODataCollectionReaderState.Start"); Debug.Assert(this.IsReadingNestedPayload || this.verboseJsonCollectionDeserializer.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None when not reading a nested payload."); // read the data wrapper depending on whether we are reading a request or response this.verboseJsonCollectionDeserializer.ReadPayloadStart(this.IsReadingNestedPayload); if (this.IsResultsWrapperExpected && this.verboseJsonCollectionDeserializer.JsonReader.NodeType != JsonNodeType.StartObject) { throw new ODataException(ODataErrorStrings.ODataJsonCollectionReader_CannotReadWrappedCollectionStart(this.verboseJsonCollectionDeserializer.JsonReader.NodeType)); } if (!this.IsResultsWrapperExpected && this.verboseJsonCollectionDeserializer.JsonReader.NodeType != JsonNodeType.StartArray) { throw new ODataException(ODataErrorStrings.ODataJsonCollectionReader_CannotReadCollectionStart(this.verboseJsonCollectionDeserializer.JsonReader.NodeType)); } // read the start of the collection until we find the content array ODataCollectionStart collectionStart = this.verboseJsonCollectionDeserializer.ReadCollectionStart(this.IsResultsWrapperExpected); this.verboseJsonCollectionDeserializer.JsonReader.ReadStartArray(); this.EnterScope(ODataCollectionReaderState.CollectionStart, collectionStart); return(true); }
/// <summary> /// Returns the annotation in the OData metadata namespace with the specified <paramref name="localName" />. /// </summary> /// <param name="model">The <see cref="IEdmModel"/> containing the annotation.</param> /// <param name="annotatable">The <see cref="IEdmElement"/> to get the annotation from.</param> /// <param name="localName">The local name of the annotation to find.</param> /// <param name="value">The value of the annotation in the OData metadata namespace and with the specified <paramref name="localName"/>.</param> /// <returns>true if an annotation with the specified local name was found; otherwise false.</returns> internal static bool TryGetODataAnnotation(this IEdmModel model, IEdmElement annotatable, string localName, out string value) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(model != null, "model != null"); Debug.Assert(annotatable != null, "annotatable != null"); Debug.Assert(!string.IsNullOrEmpty(localName), "!string.IsNullOrEmpty(localName)"); object annotationValue = model.GetAnnotationValue(annotatable, AtomConstants.ODataMetadataNamespace, localName); if (annotationValue == null) { value = null; return(false); } IEdmStringValue annotationStringValue = annotationValue as IEdmStringValue; if (annotationStringValue == null) { // invalid annotation type found throw new ODataException(Strings.ODataAtomWriterMetadataUtils_InvalidAnnotationValue(localName, annotationValue.GetType().FullName)); } value = annotationStringValue.Value; return(true); }
/// <summary> /// Parses a segment; a expression that is followed by a slash. /// </summary> /// <param name="parent">The parent of the segment node.</param> /// <returns>The lexical token representing the segment.</returns> private PathSegmentToken ParseSegment(PathSegmentToken parent) { string propertyName; if (this.Lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.Lexer.ReadDottedIdentifier(this.isSelect); } else if (this.Lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { if (this.Lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.Lexer.Position)); } propertyName = this.Lexer.CurrentToken.Text; this.Lexer.NextToken(); } else { propertyName = this.Lexer.CurrentToken.GetIdentifier(); this.Lexer.NextToken(); } return(new NonSystemToken(propertyName, null, parent)); }
/// <summary> /// Bind this function call token as a built in function /// </summary> /// <param name="functionCallToken">the function call token to bidn</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="argumentNodes">list of semantically bound arguments</param> /// <returns>A function call node bound to this function.</returns> private static QueryNode BindAsBuiltInFunction(FunctionCallToken functionCallToken, BindingState state, List <QueryNode> argumentNodes) { if (functionCallToken.Source != null) { // the parent must be null for a built in function. throw new ODataException(ODataErrorStrings.FunctionCallBinder_BuiltInFunctionMustHaveHaveNullParent(functionCallToken.Name)); } // There are some functions (IsOf and Cast for example) that don't necessarily need to be bound to a BuiltInFunctionSignature, // for these, we just Bind them directly to a SingleValueFunctionCallNode if (IsUnboundFunction(functionCallToken.Name)) { return(CreateUnboundFunctionNode(functionCallToken, argumentNodes, state)); } // Do some validation and get potential built-in functions that could match what we saw FunctionSignatureWithReturnType[] signatures = GetBuiltInFunctionSignatures(functionCallToken.Name); IEdmTypeReference[] argumentTypes = EnsureArgumentsAreSingleValue(functionCallToken.Name, argumentNodes); FunctionSignatureWithReturnType signature = MatchSignatureToBuiltInFunction(functionCallToken.Name, argumentTypes, signatures); if (signature.ReturnType != null) { TypePromoteArguments(signature, argumentNodes); } IEdmTypeReference returnType = signature.ReturnType; return(new SingleValueFunctionCallNode(functionCallToken.Name, new ReadOnlyCollection <QueryNode>(argumentNodes), returnType)); }
/// <summary> /// Ensure that this expand path contains only valid segment types. /// </summary> /// <exception cref="ODataException">Throws if this list of segments doesn't match the requirements for a $expand</exception> private void ValidatePath() { int index = 0; bool foundNavProp = false; foreach (ODataPathSegment segment in this) { if (segment is TypeSegment) { if (index == this.Count - 1) { throw new ODataException(ODataErrorStrings.ODataExpandPath_OnlyLastSegmentMustBeNavigationProperty); } } else if (segment is NavigationPropertySegment) { if (index < this.Count - 1 || foundNavProp) { throw new ODataException(ODataErrorStrings.ODataExpandPath_OnlyLastSegmentMustBeNavigationProperty); } foundNavProp = true; } else { throw new ODataException(ODataErrorStrings.ODataExpandPath_InvalidExpandPathSegment(segment.GetType().Name)); } index++; } }
/// <summary> /// Validate the Metadata Uri Fragment is @Element for a non $links metadata uri, throws if its not correct /// </summary> /// <param name="elementSelector">Element selector.</param> private void ValidateMetadataUriFragmentItemSelector(string elementSelector) { if (string.CompareOrdinal(JsonLightConstants.MetadataUriFragmentItemSelector, elementSelector) != 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidEntityWithTypeCastUriSuffix(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), elementSelector, JsonLightConstants.MetadataUriFragmentItemSelector)); } }
/// <summary> /// Returns the type of the property on the specified type. /// </summary> /// <param name="type">The type to look for the property on.</param> /// <param name="propertyName">The name of the property to look for.</param> /// <returns>The type of the property specified.</returns> private static IEdmTypeReference GetPropertyType(IEdmType type, string propertyName) { Debug.Assert(propertyName != null, "propertyName != null"); IEdmStructuredType structuredType = type as IEdmStructuredType; IEdmProperty property = structuredType == null ? null : structuredType.FindProperty(propertyName); if (property != null) { IEdmTypeReference propertyType = property.Type; if (propertyType.IsNonEntityCollectionType()) { throw new ODataException(ODataErrorStrings.EpmSourceTree_CollectionPropertyCannotBeMapped(propertyName, type.ODataFullName())); } if (propertyType.IsStream()) { throw new ODataException(ODataErrorStrings.EpmSourceTree_StreamPropertyCannotBeMapped(propertyName, type.ODataFullName())); } if (propertyType.IsSpatial()) { throw new ODataException(ODataErrorStrings.EpmSourceTree_SpatialTypeCannotBeMapped(propertyName, type.ODataFullName())); } return(property.Type); } if (type != null && !type.IsOpenType()) { throw new ODataException(ODataErrorStrings.EpmSourceTree_MissingPropertyOnType(propertyName, type.ODataFullName())); } return(null); }
/// <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) { DebugUtils.CheckNoExternalCallers(); 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.ODataNamespace), "The 'element' node should be in the OData Namespace or in the user specified 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)); } // We don't support EPM for collections so it is fine to say that EPM is not present object item = this.ReadNonEntityValue(expectedItemType, this.duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /* epmPresent */ false); // 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); }
/// <summary> /// This method creates and reads the property from the input and /// returns an <see cref="ODataProperty"/> representing the read property. /// </summary> /// <param name="expectedProperty">The <see cref="IEdmProperty"/> producing the property to be read.</param> /// <param name="expectedPropertyTypeReference">The expected type of the property to read.</param> /// <returns>An <see cref="ODataProperty"/> representing the read property.</returns> internal ODataProperty ReadTopLevelProperty(IEdmStructuralProperty expectedProperty, IEdmTypeReference expectedPropertyTypeReference) { DebugUtils.CheckNoExternalCallers(); Debug.Assert( expectedPropertyTypeReference == null || !expectedPropertyTypeReference.IsODataEntityTypeKind(), "If the expected type is specified it must not be an entity type."); Debug.Assert(this.XmlReader != null, "this.xmlReader != null"); this.ReadPayloadStart(); Debug.Assert(this.XmlReader.NodeType == XmlNodeType.Element, "The XML reader must be positioned on an Element."); // For compatibility with WCF DS Server we need to be able to read the property element in any namespace, not just the OData namespace. if (!this.UseServerFormatBehavior && !this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_TopLevelPropertyElementWrongNamespace(this.XmlReader.NamespaceURI, this.XmlReader.ODataNamespace)); } // this is a top level property so EPM does not apply hence it is safe to say that EPM is not present this.AssertRecursionDepthIsZero(); string expectedPropertyName = ReaderUtils.GetExpectedPropertyName(expectedProperty); ODataProperty property = this.ReadProperty( expectedPropertyName, expectedPropertyTypeReference, /*nullValueReadBehaviorKind*/ ODataNullValueBehaviorKind.Default, /* epmPresent */ false); this.AssertRecursionDepthIsZero(); Debug.Assert(property != null, "If we don't ignore null values the property must not be null."); this.ReadPayloadEnd(); return(property); }
/// <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)); } // a query option looks like // <anytext>; // advance the lexer to the beginning of the query option. this.Lexer.NextToken(); // get the full text from the current location onward string textToReturn = this.Lexer.ExpressionText.Substring(this.Lexer.Position); textToReturn = textToReturn.Split(';').First(); while (this.Lexer.PeekNextToken().Kind != ExpressionTokenKind.SemiColon) { this.Lexer.NextToken(); } // next token is a semicolon, so advance there this.Lexer.NextToken(); return(textToReturn); }
/// <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.Identifier: { IdentifierTokenizer identifierTokenizer = new IdentifierTokenizer(this.parameters, new FunctionCallParser(this.lexer, this.ParseExpression)); return(identifierTokenizer.ParseIdentifier(null)); } case ExpressionTokenKind.OpenParen: { return(this.ParseParenExpression()); } case ExpressionTokenKind.Star: { IdentifierTokenizer identifierTokenizer = new IdentifierTokenizer(this.parameters, new FunctionCallParser(this.lexer, this.ParseExpression)); 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); } } }
/// <summary> /// Compute the result type of a binary operator based on the type of its operands and the operator kind. /// </summary> /// <param name="typeReference">The type reference of the operators.</param> /// <param name="operatorKind">The kind of operator.</param> /// <returns>The result type reference of the binary operator.</returns> internal static IEdmPrimitiveTypeReference GetBinaryOperatorResultType(IEdmPrimitiveTypeReference typeReference, BinaryOperatorKind operatorKind) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(typeReference != null, "type != null"); 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: return(EdmCoreModel.Instance.GetBoolean(typeReference.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(typeReference); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.QueryNodeUtils_BinaryOperatorResultType_UnreachableCodepath)); } }
/// <summary> /// Creates a text ATOM value. /// </summary> /// <param name="textValue">The text value to use.</param> /// <param name="contentKind">The content kind of the value.</param> /// <returns>The Atom text value.</returns> private static AtomTextConstruct CreateAtomTextConstruct(string textValue, SyndicationTextContentKind contentKind) { AtomTextConstructKind kind; switch (contentKind) { case SyndicationTextContentKind.Plaintext: kind = AtomTextConstructKind.Text; break; case SyndicationTextContentKind.Html: kind = AtomTextConstructKind.Html; break; case SyndicationTextContentKind.Xhtml: kind = AtomTextConstructKind.Xhtml; break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_CreateAtomTextConstruct)); } return(new AtomTextConstruct { Kind = kind, Text = textValue, }); }
/// <summary> /// Converts the given <paramref name="uri"/> Uri to a string. /// If the provided baseUri is not null and is a base Uri of the <paramref name="uri"/> Uri /// the method returns the string form of the relative Uri. /// </summary> /// <param name="uri">The Uri to convert.</param> /// <param name="failOnRelativeUriWithoutBaseUri">If set to true then this method will fail if the uri specified by <paramref name="uri"/> is relative /// and no base uri is specified.</param> /// <returns>The string form of the <paramref name="uri"/> Uri. If the Uri is absolute it returns the /// string form of the <paramref name="uri"/>. If the <paramref name="uri"/> Uri is not absolute /// it returns the original string of the Uri.</returns> internal string UriToUrlAttributeValue(Uri uri, bool failOnRelativeUriWithoutBaseUri) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(uri != null, "uri != null"); if (this.UrlResolver != null) { // The resolver returns 'null' if no custom resolution is desired. Uri resultUri = this.UrlResolver.ResolveUrl(this.MessageWriterSettings.BaseUri, uri); if (resultUri != null) { return(UriUtilsCommon.UriToString(resultUri)); } } if (!uri.IsAbsoluteUri) { // NOTE: the only URIs that are allowed to be relative (e.g., failOnRelativeUriWithoutBaseUri is false) // are metadata URIs in operations; for such metadata URIs there is no base URI. if (this.MessageWriterSettings.BaseUri == null && failOnRelativeUriWithoutBaseUri) { throw new ODataException( ODataErrorStrings.ODataWriter_RelativeUriUsedWithoutBaseUriSpecified(UriUtilsCommon.UriToString(uri))); } uri = UriUtils.EnsureEscapedRelativeUri(uri); } return(UriUtilsCommon.UriToString(uri)); }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="entityType">the entity type of the current scope based on type segments.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmEntityType entityType) { ODataPathSegment nextSegment; if (TryBindAsDeclaredProperty(tokenIn, entityType, out nextSegment)) { return(nextSegment); } // for open types, 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 (!entityType.IsOpen || tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, entityType, out nextSegment)) { return(nextSegment); } } if (entityType.IsOpen) { return(new OpenPropertySegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(entityType.FullName(), tokenIn.Identifier)); }
/// <summary> /// Resolves a name to an <see cref="IEdmFunctionImport"/> instance. /// </summary> /// <param name="model">The model to resolve the name against.</param> /// <param name="operationName">The name of the service operation to look up.</param> /// <returns>An <see cref="IEdmFunctionImport"/> instance with the specified <paramref name="operationName"/> or null if no such service operation exists.</returns> public static IEdmFunctionImport TryResolveServiceOperation(this IEdmModel model, string operationName) { ExceptionUtils.CheckArgumentNotNull(model, "model"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(operationName, "operationName"); IEdmFunctionImport functionImport = null; foreach (IEdmFunctionImport import in model.ResolveFunctionImports(operationName)) { if (model.IsServiceOperation(import)) { if (functionImport == null) { functionImport = import; } else { throw new ODataException( ODataErrorStrings.ODataQueryUtils_FoundMultipleServiceOperations(operationName)); } } } return(functionImport); }
/// <summary> /// Gets the correct set of function signatures for type promotion for a given binary operator. /// </summary> /// <param name="operatorKind">The operator kind to get the signatures for.</param> /// <returns>The set of signatures for the specified <paramref name="operatorKind"/>.</returns> private static FunctionSignature[] GetFunctionSignatures(BinaryOperatorKind operatorKind) { switch (operatorKind) { case BinaryOperatorKind.Or: // fall through case BinaryOperatorKind.And: return(logicalSignatures); 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: return(relationalSignatures); case BinaryOperatorKind.Add: // fall through case BinaryOperatorKind.Subtract: // fall through case BinaryOperatorKind.Multiply: // fall through case BinaryOperatorKind.Divide: // fall through case BinaryOperatorKind.Modulo: // fall through return(arithmeticSignatures); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.TypePromotionUtils_GetFunctionSignatures_Binary_UnreachableCodepath)); } }
/// <summary> /// Parses argument lists. /// </summary> /// <returns>The lexical tokens representing the arguments.</returns> public FunctionParameterToken[] ParseArgumentList() { if (this.Lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen) { throw new ODataException(ODataErrorStrings.UriQueryExpressionParser_OpenParenExpected(this.Lexer.CurrentToken.Position, this.Lexer.ExpressionText)); } this.Lexer.NextToken(); FunctionParameterToken[] arguments; if (this.Lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { arguments = FunctionParameterToken.EmptyParameterList; } else { arguments = this.ParseArguments(); } if (this.Lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { throw new ODataException(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.Lexer.CurrentToken.Position, this.Lexer.ExpressionText)); } this.Lexer.NextToken(); return(arguments); }
/// <summary> /// Binds a DottedIdentifierToken and it's parent node (if needed). /// </summary> /// <param name="dottedIdentifierToken">Token to bind to metadata.</param> /// <param name="state">State of the Binding.</param> /// <returns>A bound node representing the cast.</returns> internal QueryNode BindDottedIdentifier(DottedIdentifierToken dottedIdentifierToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(dottedIdentifierToken, "castToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); QueryNode parent; IEdmType parentType; if (dottedIdentifierToken.NextToken == null) { parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable); parentType = state.ImplicitRangeVariable.TypeReference.Definition; } else { parent = this.bindMethod(dottedIdentifierToken.NextToken); parentType = parent.GetEdmType(); } SingleEntityNode parentAsSingleValue = parent as SingleEntityNode; IEdmSchemaType childType = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier); IEdmEntityType childEntityType = childType as IEdmEntityType; if (childEntityType == null) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod); QueryNode functionCallNode; if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, parentAsSingleValue, state, out functionCallNode)) { return(functionCallNode); } else { throw new ODataException(ODataErrorStrings.CastBinder_ChildTypeIsNotEntity(dottedIdentifierToken.Identifier)); } } // Check whether childType is a derived type of the type of its parent node UriEdmHelpers.CheckRelatedTo(parentType, childType); EntityCollectionNode parentAsCollection = parent as EntityCollectionNode; if (parentAsCollection != null) { return(new EntityCollectionCastNode(parentAsCollection, childEntityType)); } // parent can be null for casts on the implicit parameter; this is OK if (parent == null) { return(new SingleEntityCastNode(null, childEntityType)); } Debug.Assert(parentAsSingleValue != null, "If parent of the cast node was not collection, it should be a single value."); return(new SingleEntityCastNode(parentAsSingleValue, childEntityType)); }
/// <summary> /// Validates that the count property in an OData-owned object wrapper is valid. /// </summary> /// <param name="propertyValue">The value of the property.</param> internal static void ValidateCountPropertyInEntityReferenceLinks(long?propertyValue) { DebugUtils.CheckNoExternalCallers(); if (!propertyValue.HasValue) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_EntityReferenceLinksInlineCountWithNullValue(JsonConstants.ODataCountName)); } }
/// <summary> /// Parses the Any/All portion of the query /// </summary> /// <param name="parent">The parent of the Any/All node.</param> /// <param name="isAny">Denotes whether an Any or All is to be parsed.</param> /// <returns>The lexical token representing the Any/All query.</returns> private QueryToken ParseAnyAll(QueryToken parent, bool isAny) { 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(); // When faced with Any(), return the same thing as if you encountered Any(a : true) if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { this.lexer.NextToken(); if (isAny) { return(new AnyToken(new LiteralToken(true, "True"), null, parent)); } else { return(new AllToken(new LiteralToken(true, "True"), null, parent)); } } string parameter = this.lexer.CurrentToken.GetIdentifier(); if (!this.parameters.Add(parameter)) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_RangeVariableAlreadyDeclared(parameter)); } // read the ':' separating the range variable from the expression. this.lexer.NextToken(); this.lexer.ValidateToken(ExpressionTokenKind.Colon); this.lexer.NextToken(); QueryToken expr = this.ParseExpression(); if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } // forget about the range variable after parsing the expression for this lambda. this.parameters.Remove(parameter); this.lexer.NextToken(); if (isAny) { return(new AnyToken(expr, parameter, parent)); } else { return(new AllToken(expr, parameter, parent)); } }
/// <summary> /// Tries to parse a collection of function parameters. Allows path and filter to share the core algorithm while representing parameters differently. /// </summary> /// <param name="lexer">The lexer to read from.</param> /// <param name="endTokenKind">The token kind that marks the end of the parameters.</param> /// <param name="splitParameters">The parameters if they were successfully split.</param> /// <returns>Whether the parameters could be split.</returns> private static bool TrySplitFunctionParameters(this ExpressionLexer lexer, ExpressionTokenKind endTokenKind, out ICollection <FunctionParameterToken> splitParameters) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(lexer != null, "lexer != null"); var parameters = new List <FunctionParameterToken>(); splitParameters = parameters; ExpressionToken currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { return(true); } if (currentToken.Kind != ExpressionTokenKind.Identifier || lexer.PeekNextToken().Kind != ExpressionTokenKind.Equal) { return(false); } while (currentToken.Kind != endTokenKind) { lexer.ValidateToken(ExpressionTokenKind.Identifier); string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); lexer.ValidateToken(ExpressionTokenKind.Equal); lexer.NextToken(); QueryToken parameterValue; if (!TryCreateParameterValueToken(lexer.CurrentToken, out parameterValue)) { throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } parameters.Add(new FunctionParameterToken(identifier, parameterValue)); // Read the next parameterToken. We should be at the end, or find // we have a comma followed by something. lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == ExpressionTokenKind.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { // Trailing comma. throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } } } return(true); }
private static DateTimeOffset CreateDateTimeValue(object propertyValue, SyndicationItemProperty targetProperty, ODataWriterBehavior writerBehavior) { Debug.Assert( writerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesClient, "CreateDateTimeValue should not be used in WCF DS client mode."); if (propertyValue == null) { return(DateTimeOffset.Now); } if (propertyValue is DateTimeOffset) { return((DateTimeOffset)propertyValue); } if (propertyValue is DateTime) { // DateTimeOffset takes care of DateTimes of Unspecified kind so we won't end up // with datetime without timezone info mapped to atom:updated or atom:published element. return(new DateTimeOffset((DateTime)propertyValue)); } string stringValue = propertyValue as string; if (stringValue != null) { DateTimeOffset date; if (!DateTimeOffset.TryParse(stringValue, out date)) { DateTime result; if (!DateTime.TryParse(stringValue, out result)) { throw new ODataException(ODataErrorStrings.EpmSyndicationWriter_DateTimePropertyCanNotBeConverted(targetProperty.ToString())); } return(new DateTimeOffset(result)); } return(date); } try { return(new DateTimeOffset(Convert.ToDateTime(propertyValue, CultureInfo.InvariantCulture))); } catch (Exception e) { if (!ExceptionUtils.IsCatchableExceptionType(e)) { throw; } throw new ODataException(ODataErrorStrings.EpmSyndicationWriter_DateTimePropertyCanNotBeConverted(targetProperty.ToString())); } }
/// <summary> /// Validates that the property in feed wrapper is valid. /// </summary> /// <param name="propertyValue">The value of the property.</param> /// <param name="propertyName">The name of the property (used for error reporting).</param> internal static void ValidateFeedProperty(object propertyValue, string propertyName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); if (propertyValue == null) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_FeedPropertyWithNullValue(propertyName)); } }
/// <summary> /// Build the list of expand options /// Depends on whether options are allowed or not. /// </summary> /// <param name="isInnerTerm">is this an inner expand term</param> /// <param name="pathToken">the current level token, as a PathToken</param> /// <returns>An expand term token based on the path token.</returns> internal override ExpandTermToken BuildExpandTermToken(bool isInnerTerm, PathSegmentToken pathToken) { DebugUtils.CheckNoExternalCallers(); if (this.IsNotEndOfTerm(false)) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.Lexer.ExpressionText)); } return(new ExpandTermToken(pathToken)); }
/// <summary> /// Parses * (all member) access at the beginning of a select expression. /// </summary> /// <param name="instance">Instance being accessed.</param> /// <returns>The lexical token representing the expression.</returns> public QueryToken ParseStarMemberAccess(QueryToken instance) { if (this.lexer.CurrentToken.Text != UriQueryConstants.Star) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CannotCreateStarTokenFromNonStar(this.lexer.CurrentToken.Text)); } this.lexer.NextToken(); return(new StarToken(instance)); }
/// <summary> /// Validates that the annotation string value is valid. /// </summary> /// <param name="propertyValue">The value of the annotation.</param> /// <param name="annotationName">The name of the (instance or property) annotation (used for error reporting).</param> internal static void ValidateAnnotationStringValue(string propertyValue, string annotationName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(!string.IsNullOrEmpty(annotationName), "!string.IsNullOrEmpty(annotationName)"); if (propertyValue == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightReaderUtils_AnnotationWithNullValue(annotationName)); } }
/// <summary> /// Reads EPM values from a person construct (author or contributor). /// </summary> /// <param name="targetList">The target list, this can be either a list of properties (on entry or complex value), /// or a list of items (for a collection of primitive types).</param> /// <param name="targetTypeReference">The type of the value on which to set the property (can be entity, complex or primitive).</param> /// <param name="targetSegment">The target segment which points to either author or contributor element.</param> /// <param name="personMetadata">The person ATOM metadata to read from.</param> private void ReadPersonEpm(ReadOnlyEnumerable <ODataProperty> targetList, IEdmTypeReference targetTypeReference, EpmTargetPathSegment targetSegment, AtomPersonMetadata personMetadata) { Debug.Assert(targetList != null, "targetList != null"); Debug.Assert(targetTypeReference != null, "targetTypeReference != null"); Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert( targetSegment.SegmentName == AtomConstants.AtomAuthorElementName || targetSegment.SegmentName == AtomConstants.AtomContributorElementName, "targetSegment must be author or contributor."); Debug.Assert(personMetadata != null, "personMetadata != null"); foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments) { Debug.Assert(subSegment.HasContent, "sub segment of author segment must have content, there are no subsegments which don't have content under author."); Debug.Assert( subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null && subSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty, "We should never find a subsegment without EPM attribute or for custom mapping when writing syndication person EPM."); switch (subSegment.EpmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.AuthorName: case SyndicationItemProperty.ContributorName: // Note that person name can never specify true null in EPM, since it can't have the m:null attribute on it. string personName = personMetadata.Name; if (personName != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personName); } break; case SyndicationItemProperty.AuthorEmail: case SyndicationItemProperty.ContributorEmail: string personEmail = personMetadata.Email; if (personEmail != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personEmail); } break; case SyndicationItemProperty.AuthorUri: case SyndicationItemProperty.ContributorUri: string personUri = personMetadata.UriFromEpm; if (personUri != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personUri); } break; default: // Unhandled EpmTargetPathSegment.SegmentName. throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadPersonEpm)); } } }