/// <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));
            }
        }
Ejemplo n.º 2
0
        /// <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();
            }
        }
Ejemplo n.º 4
0
        /// <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);
        }
Ejemplo n.º 7
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));
            }
        }
        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);
            }
        }
Ejemplo n.º 9
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]);
        }
Ejemplo n.º 10
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);
        }
Ejemplo n.º 13
0
        /// <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);
        }