/// <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) { 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: case BinaryOperatorKind.Has: 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> /// 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: return AdditionSignatures; case BinaryOperatorKind.Subtract: return SubtractionSignatures; 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> /// Reads the end of a collection; this includes collection-level instance annotations. /// </summary> /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param> /// <remarks> /// Pre-Condition: EndArray node: End of the collection content array /// Post-Condition: EndOfInput: All of the collection payload has been consumed. /// </remarks> internal void ReadCollectionEnd(bool isReadingNestedPayload) { Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "Pre-condition: JsonNodeType.EndArray"); this.JsonReader.AssertNotBuffering(); this.JsonReader.ReadEndArray(); if (!isReadingNestedPayload) { // Create a new duplicate property names checker object here; we don't have to use the one from reading the // collection start since we don't allow any annotations/properties after the collection property. DuplicatePropertyNamesChecker collectionEndDuplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); // Fail on anything after the collection that is not a custom instance annotation while (this.JsonReader.NodeType == JsonNodeType.Property) { this.ProcessProperty( collectionEndDuplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { // This method will allow and skip over any custom annotations, but will not report them as enum values, so any result we get other than EndOfObject indicates a malformed payload. switch (propertyParsingResult) { case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.ODataInstanceAnnotation: if (!IsValidODataAnnotationOfCollection(propertyName)) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionEnd(propertyName)); } this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithoutValue: // fall through case PropertyParsingResult.PropertyWithValue: // fall through case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionEnd(propertyName)); case PropertyParsingResult.EndOfObject: break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionEnd)); } }); } // read the end-object node of the value containing the 'value' property this.JsonReader.ReadEndObject(); } }
/// <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(UnaryOperatorKind operatorKind) { switch (operatorKind) { case UnaryOperatorKind.Negate: return negationSignatures; case UnaryOperatorKind.Not: return notSignatures; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.TypePromotionUtils_GetFunctionSignatures_Unary_UnreachableCodepath)); } }
/// <summary> /// Gets the payload type name for an OData OM instance for JsonLight. /// </summary> /// <param name="payloadItem">The payload item to get the type name for.</param> /// <returns>The type name as read from the payload item (or constructed for primitive items).</returns> internal static string GetPayloadTypeName(object payloadItem) { if (payloadItem == null) { return(null); } TypeCode typeCode = ODataPlatformHelper.GetTypeCode(payloadItem.GetType()); switch (typeCode) { // In JSON only boolean, String, Int32 and Double are recognized as primitive types // (without additional type conversion). So only check for those; if not one of these primitive // types it must be a complex, entity or collection value. case TypeCode.Boolean: return(Metadata.EdmConstants.EdmBooleanTypeName); case TypeCode.String: return(Metadata.EdmConstants.EdmStringTypeName); case TypeCode.Int32: return(Metadata.EdmConstants.EdmInt32TypeName); case TypeCode.Double: return(Metadata.EdmConstants.EdmDoubleTypeName); default: Debug.Assert(typeCode == TypeCode.Object, "If not one of the primitive types above, it must be an object in JSON."); break; } ODataComplexValue complexValue = payloadItem as ODataComplexValue; if (complexValue != null) { return(complexValue.TypeName); } ODataCollectionValue collectionValue = payloadItem as ODataCollectionValue; if (collectionValue != null) { return(EdmLibraryExtensions.GetCollectionTypeFullName(collectionValue.TypeName)); } ODataEntry entry = payloadItem as ODataEntry; if (entry != null) { return(entry.TypeName); } throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightReader_ReadEntryStart)); }
/// <summary> /// Parses the eq, ne, lt, gt, le, ge operators. /// </summary> /// <returns>The lexical token representing the expression.</returns> private QueryToken ParseComparison() { this.RecurseEnter(); QueryToken left = this.ParseAdditive(); while (this.lexer.CurrentToken.IsComparisonOperator) { BinaryOperatorKind binaryOperatorKind; switch (this.lexer.CurrentToken.Text) { case ExpressionConstants.KeywordEqual: binaryOperatorKind = BinaryOperatorKind.Equal; break; case ExpressionConstants.KeywordNotEqual: binaryOperatorKind = BinaryOperatorKind.NotEqual; break; case ExpressionConstants.KeywordGreaterThan: binaryOperatorKind = BinaryOperatorKind.GreaterThan; break; case ExpressionConstants.KeywordGreaterThanOrEqual: binaryOperatorKind = BinaryOperatorKind.GreaterThanOrEqual; break; case ExpressionConstants.KeywordLessThan: binaryOperatorKind = BinaryOperatorKind.LessThan; break; case ExpressionConstants.KeywordLessThanOrEqual: binaryOperatorKind = BinaryOperatorKind.LessThanOrEqual; break; case ExpressionConstants.KeywordHas: binaryOperatorKind = BinaryOperatorKind.Has; break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriQueryExpressionParser_ParseComparison)); } this.lexer.NextToken(); QueryToken right = this.ParseAdditive(); left = new BinaryOperatorToken(binaryOperatorKind, left, right); } this.RecurseLeave(); return(left); }
/// <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)); } }
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); } }
/// <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]); }
/// <summary> /// Reads the entity reference link instance annotations. /// </summary> /// <param name="links">The <see cref="ODataEntityReferenceLinks"/> to read the annotations for.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the entity reference links scope.</param> /// <param name="forLinksStart">true when parsing the instance annotations before the 'value' property; /// false when parsing the instance annotations after the 'value' property.</param> /// <remarks> /// Pre-Condition: JsonNodeType.Property The first property in the payload (or the first property after the context URI in responses) /// JsonNodeType.EndObject The end of the entity reference links object /// Post-Condition: JsonNodeType.EndObject When the end of the entity reference links object is reached /// Any The first node of the value of the 'url' property (if found) /// </remarks> private void ReadEntityReferenceLinksAnnotations(ODataEntityReferenceLinks links, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool forLinksStart) { Debug.Assert(links != null, "links != null"); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); this.JsonReader.AssertNotBuffering(); while (this.JsonReader.NodeType == JsonNodeType.Property) { // OData property annotations are not supported on entity reference links. Func <string, object> propertyAnnotationValueReader = annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLinks); }; bool foundValueProperty = false; this.ProcessProperty( duplicatePropertyNamesChecker, propertyAnnotationValueReader, (propertyParseResult, propertyName) => { switch (propertyParseResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (string.CompareOrdinal(ODataAnnotationNames.ODataNextLink, propertyName) == 0) { this.ReadEntityReferenceLinksNextLinkAnnotationValue(links); } else if (string.CompareOrdinal(ODataAnnotationNames.ODataCount, propertyName) == 0) { this.ReadEntityReferenceCountAnnotationValue(links); } else { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); } break; case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0) { // We did not find a supported link collection property; fail. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidEntityReferenceLinksPropertyFound(propertyName, JsonLightConstants.ODataValuePropertyName)); } // We found the link collection property and are done parsing property annotations; foundValueProperty = true; break; case PropertyParsingResult.PropertyWithoutValue: // If we find a property without a value it means that we did not find the entity reference links property (yet) // but an invalid property annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyAnnotationInEntityReferenceLinks(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadEntityReferenceLinksAnnotations)); } }); if (foundValueProperty) { return; } } if (forLinksStart) { // We did not find the 'value' property. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_ExpectedEntityReferenceLinksPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } this.AssertJsonCondition(JsonNodeType.EndObject); }
/// <summary> /// Returns the 4 bits that correspond to the specified character. /// </summary> /// <param name="c">Character in the 0-F range to be converted.</param> /// <returns>The 4 bits that correspond to the specified character.</returns> /// <exception cref="FormatException">Thrown when 'c' is not in the '0'-'9','a'-'f' range.</exception> /// <remarks>This is a copy of WebConvert.HexCharToNibble.</remarks> private static byte HexCharToNibble(char c) { Debug.Assert(IsCharHexDigit(c), string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} is not a hex digit.", c)); switch (c) { case '0': return(0); case '1': return(1); case '2': return(2); case '3': return(3); case '4': return(4); case '5': return(5); case '6': return(6); case '7': return(7); case '8': return(8); case '9': return(9); case 'a': case 'A': return(10); case 'b': case 'B': return(11); case 'c': case 'C': return(12); case 'd': case 'D': return(13); case 'e': case 'E': return(14); case 'f': case 'F': return(15); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_HexCharToNibble)); } }
/// <summary> /// Reads the primitive, complex or collection value. /// </summary> /// <param name="expectedTypeReference">The expected type reference of the value.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <param name="validateNullValue">true to validate a null value (i.e., throw if a null value is being written for a non-nullable property); otherwise false.</param> /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param> /// <returns>The value read (null, primitive CLR value, ODataComplexValue or ODataCollectionValue).</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The XML element containing the value to read (also the attributes will be read from it) /// Post-Condition: XmlNodeType.EndElement - The end tag of the element. /// XmlNodeType.Element - The empty element node. /// </remarks> private object ReadNonEntityValueImplementation(IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, string propertyName) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( expectedTypeReference == null || !expectedTypeReference.IsODataEntityTypeKind(), "Only primitive, complex or collection types can be read by this method."); Debug.Assert( expectedTypeReference == null || collectionValidator == null, "If an expected value type reference is specified, no collection validator must be provided."); this.XmlReader.AssertNotBuffering(); // Read the attributes looking for m:type and m:null string payloadTypeName; bool isNull; this.ReadNonEntityValueAttributes(out payloadTypeName, out isNull); object result; if (isNull) { result = this.ReadNullValue(expectedTypeReference, validateNullValue, propertyName); } else { // If we could derive the item type name from the collection's type name and no type name was specified in the payload // fill it in now. EdmTypeKind payloadTypeKind; bool derivedItemTypeNameFromCollectionTypeName = false; if (collectionValidator != null && payloadTypeName == null) { payloadTypeName = collectionValidator.ItemTypeNameFromCollection; payloadTypeKind = collectionValidator.ItemTypeKindFromCollection; derivedItemTypeNameFromCollectionTypeName = payloadTypeKind != EdmTypeKind.None; } // Resolve the payload type name and compute the target type kind and target type reference. SerializationTypeNameAnnotation serializationTypeNameAnnotation; EdmTypeKind targetTypeKind; IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*defaultPrimitivePayloadType*/ edmStringType, expectedTypeReference, payloadTypeName, this.Model, this.MessageReaderSettings, this.GetNonEntityValueKind, out targetTypeKind, out serializationTypeNameAnnotation); if (derivedItemTypeNameFromCollectionTypeName) { Debug.Assert( serializationTypeNameAnnotation == null, "If we derived the item type name from the collection type name we must not have created a serialization type name annotation."); serializationTypeNameAnnotation = new SerializationTypeNameAnnotation { TypeName = null }; } // If we have no expected type make sure the collection items are of the same kind and specify the same name. if (collectionValidator != null) { Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference."); collectionValidator.ValidateCollectionItem(payloadTypeName, targetTypeKind); } switch (targetTypeKind) { case EdmTypeKind.Enum: Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataEnumTypeKind(), "Expected an OData Enum type."); result = this.ReadEnumValue(targetTypeReference.AsEnum()); break; case EdmTypeKind.Primitive: Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type."); result = this.ReadPrimitiveValue(targetTypeReference.AsPrimitive()); break; case EdmTypeKind.Complex: Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type."); result = this.ReadComplexValue( targetTypeReference == null ? null : targetTypeReference.AsComplex(), payloadTypeName, serializationTypeNameAnnotation, duplicatePropertyNamesChecker); break; case EdmTypeKind.Collection: IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference); result = this.ReadCollectionValue( collectionTypeReference, payloadTypeName, serializationTypeNameAnnotation); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue)); } } this.AssertXmlCondition(true, XmlNodeType.EndElement); this.XmlReader.AssertNotBuffering(); return(result); }
/// <summary> /// Reads the start of a collection; this includes collection-level properties (e.g., the 'results' property) if the version permits it. /// </summary> /// <param name="collectionStartDuplicatePropertyNamesChecker">The duplicate property names checker used to keep track of the properties and annotations /// in the collection wrapper object.</param> /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param> /// <param name="expectedItemTypeReference">The expected item type reference or null if none is expected.</param> /// <param name="actualItemTypeReference">The validated actual item type reference (if specified in the payload) or the expected item type reference.</param> /// <returns>An <see cref="ODataCollectionStart"/> representing the collection-level information. Currently this is only the name of the collection in ATOM.</returns> /// <remarks> /// Pre-Condition: Any: the start of a nested collection value; if this is not a 'StartArray' node this method will fail. /// JsonNodeType.Property: the first property of the collection wrapper object after the context URI. /// JsonNodeType.EndObject: when the collection wrapper object has no properties (other than the context URI). /// Post-Condition: JsonNodeType.StartArray: the start of the array of the collection items. /// </remarks> internal ODataCollectionStart ReadCollectionStart( DuplicatePropertyNamesChecker collectionStartDuplicatePropertyNamesChecker, bool isReadingNestedPayload, IEdmTypeReference expectedItemTypeReference, out IEdmTypeReference actualItemTypeReference) { this.JsonReader.AssertNotBuffering(); actualItemTypeReference = expectedItemTypeReference; ODataCollectionStart collectionStart = null; if (isReadingNestedPayload) { Debug.Assert(!this.JsonLightInputContext.ReadingResponse, "Nested collections are only supported in parameter payloads in requests."); collectionStart = new ODataCollectionStart { Name = null }; } else { while (this.JsonReader.NodeType == JsonNodeType.Property) { IEdmTypeReference actualItemTypeRef = expectedItemTypeReference; this.ProcessProperty( collectionStartDuplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithoutValue: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_TopLevelPropertyAnnotationWithoutProperty(propertyName)); case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0) { throw new ODataException( ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_InvalidTopLevelPropertyName(propertyName, JsonLightConstants.ODataValuePropertyName)); } string payloadTypeName = ValidateDataPropertyTypeNameAnnotation(collectionStartDuplicatePropertyNamesChecker, propertyName); if (payloadTypeName != null) { string itemTypeName = EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); if (itemTypeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_InvalidCollectionTypeName(payloadTypeName)); } EdmTypeKind targetTypeKind; SerializationTypeNameAnnotation serializationTypeNameAnnotation; Func <EdmTypeKind> typeKindFromPayloadFunc = () => { throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart_TypeKindFromPayloadFunc)); }; actualItemTypeRef = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*defaultPrimitivePayloadType*/ null, expectedItemTypeReference, itemTypeName, this.Model, this.MessageReaderSettings, this.Version, typeKindFromPayloadFunc, out targetTypeKind, out serializationTypeNameAnnotation); } collectionStart = new ODataCollectionStart { Name = null }; break; case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart)); } }); actualItemTypeReference = actualItemTypeRef; } if (collectionStart == null) { // No collection property found; there should be exactly one property in the collection wrapper that does not have a reserved name. throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_ExpectedCollectionPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } } // at this point the reader is positioned on the start array node for the collection contents if (this.JsonReader.NodeType != JsonNodeType.StartArray) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionContentStart(this.JsonReader.NodeType)); } this.JsonReader.AssertNotBuffering(); return(collectionStart); }
/// <summary> /// Reads the next parameter from the parameters payload. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker used to read a parameter payload.</param> /// <returns>true if a parameter was read from the payload; otherwise false.</returns> /// <remarks> /// Pre-Condition: Property or EndObject the property node of the parameter to read or the end object node if there are not parameters /// Post-Condition: Property or EndObject the node after the property value of a primitive, complex or null collection parameter /// Any the start of the value representing a non-null collection parameter (the collection reader will fail if this is not a StartArray node) /// </remarks> internal bool ReadNextParameter(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); bool parameterRead = false; if (this.JsonReader.NodeType == JsonNodeType.Property) { bool foundCustomInstanceAnnotation = false; this.ProcessProperty( duplicatePropertyNamesChecker, propertyAnnotationValueReader, (propertyParsingResult, parameterName) => { switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: // OData instance annotations are not supported in parameter payloads. throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(parameterName)); case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); foundCustomInstanceAnnotation = true; break; case PropertyParsingResult.PropertyWithoutValue: throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_PropertyAnnotationWithoutPropertyForParameters(parameterName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(parameterName)); case PropertyParsingResult.PropertyWithValue: IEdmTypeReference parameterTypeReference = this.parameterReader.GetParameterTypeReference(parameterName); Debug.Assert(parameterTypeReference != null, "parameterTypeReference != null"); ODataParameterReaderState state; object parameterValue; EdmTypeKind parameterTypeKind = parameterTypeReference.TypeKind(); switch (parameterTypeKind) { case EdmTypeKind.Primitive: IEdmPrimitiveTypeReference primitiveTypeReference = parameterTypeReference.AsPrimitive(); if (primitiveTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.Stream) { throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedPrimitiveParameterType(parameterName, primitiveTypeReference.PrimitiveKind())); } parameterValue = this.ReadNonEntityValue( /*payloadTypeName*/ null, primitiveTypeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, parameterName); state = ODataParameterReaderState.Value; break; case EdmTypeKind.Enum: IEdmEnumTypeReference enumTypeReference = parameterTypeReference.AsEnum(); parameterValue = this.ReadNonEntityValue( /*payloadTypeName*/ null, enumTypeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, parameterName); state = ODataParameterReaderState.Value; break; case EdmTypeKind.TypeDefinition: IEdmTypeDefinitionReference typeDefinitionReference = parameterTypeReference.AsTypeDefinition(); parameterValue = this.ReadNonEntityValue( /*payloadTypeName*/ null, typeDefinitionReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, parameterName); state = ODataParameterReaderState.Value; break; case EdmTypeKind.Complex: parameterValue = this.ReadNonEntityValue( /*payloadTypeName*/ null, parameterTypeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, parameterName); state = ODataParameterReaderState.Value; break; case EdmTypeKind.Entity: parameterValue = null; state = ODataParameterReaderState.Entry; break; case EdmTypeKind.Collection: parameterValue = null; if (this.JsonReader.NodeType == JsonNodeType.PrimitiveValue) { // NOTE: we support null collections in parameter payloads but nowhere else. parameterValue = this.JsonReader.ReadPrimitiveValue(); if (parameterValue != null) { throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_NullCollectionExpected(JsonNodeType.PrimitiveValue, parameterValue)); } state = ODataParameterReaderState.Value; } else if (((IEdmCollectionType)parameterTypeReference.Definition).ElementType.TypeKind() == EdmTypeKind.Entity) { state = ODataParameterReaderState.Feed; } else { state = ODataParameterReaderState.Collection; } break; default: throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedParameterTypeKind(parameterName, parameterTypeReference.TypeKind())); } parameterRead = true; this.parameterReader.EnterScope(state, parameterName, parameterValue); Debug.Assert( state == ODataParameterReaderState.Collection || state == ODataParameterReaderState.Entry || state == ODataParameterReaderState.Feed || this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject, "Expected any node for a collection; 'Property' or 'EndObject' if it is a primitive or complex value."); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightParameterDeserializer_ReadNextParameter)); } }); if (foundCustomInstanceAnnotation) { return(this.ReadNextParameter(duplicatePropertyNamesChecker)); } } if (!parameterRead && this.JsonReader.NodeType == JsonNodeType.EndObject) { this.JsonReader.ReadEndObject(); this.ReadPayloadEnd(/*isReadingNestedPayload*/ false); // Pop the scope for the previous parameter if there is any if (this.parameterReader.State != ODataParameterReaderState.Start) { this.parameterReader.PopScope(this.parameterReader.State); } // Pop the 'Start' scope and enter the 'Completed' scope this.parameterReader.PopScope(ODataParameterReaderState.Start); this.parameterReader.EnterScope(ODataParameterReaderState.Completed, /*parameterName*/ null, /*parameterValue*/ null); this.AssertJsonCondition(JsonNodeType.EndOfInput); } return(parameterRead); }