Skip to content

Commit

Permalink
Implement type facets promotion in URI parsing #654
Browse files Browse the repository at this point in the history
  • Loading branch information
Lingxi-Li committed Aug 19, 2016
1 parent a40ff41 commit 63a66b6
Show file tree
Hide file tree
Showing 14 changed files with 836 additions and 143 deletions.
15 changes: 15 additions & 0 deletions src/Common.Stylecop
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
</Analyzer>
<Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.MaintainabilityRules">
<Rules>
<Rule Name="DebugAssertMustProvideMessageText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileMayOnlyContainASingleClass">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
Expand Down Expand Up @@ -135,6 +140,11 @@
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParameterMustNotSpanMultipleLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DoNotPlaceRegionsWithinElements">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
Expand Down Expand Up @@ -185,6 +195,11 @@
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="CodeMustNotContainMultipleWhitespaceInARow">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@
<Compile Include="..\UriParser\FunctionSignatureWithReturnType.cs">
<Link>Microsoft\OData\Core\UriParser\FunctionSignatureWithReturnType.cs</Link>
</Compile>
<Compile Include="..\UriParser\TypeFacetsPromotionRules.cs">
<Link>Microsoft\OData\Core\UriParser\TypeFacetsPromotionRules.cs</Link>
</Compile>
<Compile Include="..\ValidatorFactory.cs">
<Link>ValidatorFactory.cs</Link>
</Compile>
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.OData.Core/Microsoft.OData.Core.Net45.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@
<Compile Include="UriParser\TreeNodeKinds\QueryTokenKind.cs" />
<Compile Include="UriParser\TreeNodeKinds\RequestTargetKind.cs" />
<Compile Include="UriParser\TreeNodeKinds\UnaryOperatorKind.cs" />
<Compile Include="UriParser\TypeFacetsPromotionRules.cs" />
<Compile Include="UriParser\TypePromotionUtils.cs" />
<Compile Include="UriParser\UriFunctionsHelper.cs" />
<Compile Include="UriParser\UriQueryConstants.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@
<Compile Include="UriParser\TreeNodeKinds\QueryTokenKind.cs" />
<Compile Include="UriParser\TreeNodeKinds\RequestTargetKind.cs" />
<Compile Include="UriParser\TreeNodeKinds\UnaryOperatorKind.cs" />
<Compile Include="UriParser\TypeFacetsPromotionRules.cs" />
<Compile Include="UriParser\TypePromotionUtils.cs" />
<Compile Include="UriParser\UriFunctionsHelper.cs" />
<Compile Include="UriParser\UriQueryConstants.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@
<Compile Include="UriParser\TreeNodeKinds\QueryTokenKind.cs" />
<Compile Include="UriParser\TreeNodeKinds\RequestTargetKind.cs" />
<Compile Include="UriParser\TreeNodeKinds\UnaryOperatorKind.cs" />
<Compile Include="UriParser\TypeFacetsPromotionRules.cs" />
<Compile Include="UriParser\TypePromotionUtils.cs" />
<Compile Include="UriParser\UriFunctionsHelper.cs" />
<Compile Include="UriParser\UriQueryConstants.cs" />
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.OData.Core/Microsoft.OData.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@
<Compile Include="UriParser\Parsers\UriPathParser.cs" />
<Compile Include="UriParser\Parsers\UriQueryExpressionParser.cs" />
<Compile Include="UriParser\Parsers\UriTemplateParser.cs" />
<Compile Include="UriParser\TypeFacetsPromotionRules.cs" />
<Compile Include="UriParser\QueryNodeUtils.cs" />
<Compile Include="UriParser\QueryOptionUtils.cs" />
<Compile Include="UriParser\ReadOnlyEnumerableForUriParser.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ internal QueryNode BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)
/// <param name="binaryOperatorKind">the operator kind</param>
/// <param name="left">the left operand</param>
/// <param name="right">the right operand</param>
internal static void PromoteOperandTypes(BinaryOperatorKind binaryOperatorKind, ref SingleValueNode left, ref SingleValueNode right)
/// <param name="facetsPromotionRules">Promotion rules for type facets.</param>
internal static void PromoteOperandTypes(BinaryOperatorKind binaryOperatorKind, ref SingleValueNode left, ref SingleValueNode right, TypeFacetsPromotionRules facetsPromotionRules)
{
IEdmTypeReference leftType;
IEdmTypeReference rightType;
if (!TypePromotionUtils.PromoteOperandTypes(binaryOperatorKind, left, right, out leftType, out rightType))
if (!TypePromotionUtils.PromoteOperandTypes(binaryOperatorKind, left, right, out leftType, out rightType, facetsPromotionRules))
{
string leftTypeName = left.TypeReference == null ? "<null>" : left.TypeReference.FullName();
string rightTypeName = right.TypeReference == null ? "<null>" : right.TypeReference.FullName();
Expand Down
17 changes: 15 additions & 2 deletions src/Microsoft.OData.Core/UriParser/Binders/MetadataBindingUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal static class MetadataBindingUtils
/// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node.
/// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is.
/// </summary>
/// <param name="source">The source node to apply the convertion to.</param>
/// <param name="source">The source node to apply the conversion to.</param>
/// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param>
/// <returns>The converted query node, or the original source node unchanged.</returns>
internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference)
Expand Down Expand Up @@ -92,7 +92,20 @@ internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IE
return new ConstantNode(targetPrimitiveValue);
}

return new ConstantNode(targetPrimitiveValue, constantNode.LiteralText);
var candidate = new ConstantNode(targetPrimitiveValue, constantNode.LiteralText);
var decimalType = candidate.TypeReference as IEdmDecimalTypeReference;
if (decimalType != null)
{
var targetDecimalType = (IEdmDecimalTypeReference)targetTypeReference;
return decimalType.Precision == targetDecimalType.Precision &&
decimalType.Scale == targetDecimalType.Scale ?
(SingleValueNode)candidate :
(SingleValueNode)(new ConvertNode(candidate, targetTypeReference));
}
else
{
return candidate;
}
}
else
{
Expand Down
48 changes: 43 additions & 5 deletions src/Microsoft.OData.Core/UriParser/FunctionSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,72 @@

namespace Microsoft.OData.Core.UriParser
{
using System.Diagnostics;
using Microsoft.OData.Edm;

/// <summary>
/// Class representing a function signature using EDM types.
/// </summary>
internal sealed class FunctionSignature
{
/// <summary>The argument types for this function signature.</summary>
/// <summary>
/// The argument types for this function signature.
/// </summary>
private readonly IEdmTypeReference[] argumentTypes;

/// <summary>
/// Constructor taking all the argument types.
/// Factories for creating argument types with proper facets.
/// </summary>
private CreateArgumentTypeWithFacets[] createArgumentTypesWithFacets;

/// <summary>
/// Constructor taking all the argument types, and the factories for creating argument types with proper facets.
/// </summary>
/// <param name="argumentTypes">The argument types for this function signature.</param>
internal FunctionSignature(params IEdmTypeReference[] argumentTypes)
/// <param name="createArgumentTypesWithFacets">Factories for creating argument types with proper facets.</param>
internal FunctionSignature(
IEdmTypeReference[] argumentTypes,
CreateArgumentTypeWithFacets[] createArgumentTypesWithFacets)
{
this.argumentTypes = argumentTypes;
this.createArgumentTypesWithFacets = createArgumentTypesWithFacets;
}

/// <summary>
/// Delegate for creating an argument type with specified facets.
/// </summary>
/// <param name="precision">The precision facet.</param>
/// <param name="scale">The scale facet.</param>
/// <returns>An argument type with specified facets.</returns>
internal delegate IEdmTypeReference CreateArgumentTypeWithFacets(int? precision, int? scale);

/// <summary>
/// The argument types for this function signature.
/// </summary>
public IEdmTypeReference[] ArgumentTypes
internal IEdmTypeReference[] ArgumentTypes
{
get
{
return this.argumentTypes;
}
}

/// <summary>
/// Gets the type with specified facets for the index-th argument.
/// </summary>
/// <param name="index">Index of the argument for which to get the type for.</param>
/// <param name="precision">The precision facet.</param>
/// <param name="scale">The scale facet.</param>
/// <returns>The type with specified facets for the index-th argument.</returns>
internal IEdmTypeReference GetArgumentTypeWithFacets(int index, int? precision, int? scale)
{
if (createArgumentTypesWithFacets == null)
{
return argumentTypes[index];
}

var create = createArgumentTypesWithFacets[index];
return create != null ? create(precision, scale) : argumentTypes[index];
}
}
}
}
25 changes: 23 additions & 2 deletions src/Microsoft.OData.Core/UriParser/Metadata/ODataUriResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,35 @@ public class ODataUriResolver
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "Resolver is immutable")]
internal static readonly ODataUriResolver Default = new ODataUriResolver();

/// <summary>
/// Promotion rules for type facets.
/// </summary>
private TypeFacetsPromotionRules typeFacetsPromotionRules = new TypeFacetsPromotionRules();

/// <summary>
/// Whether to enable case insensitive for the resolver.
/// </summary>
/// <remarks>
/// All extensions should look at this property and keep case sensitive behaviour consistent.
/// All extensions should look at this property and keep case sensitive behavior consistent.
/// </remarks>
public virtual bool EnableCaseInsensitive { get; set; }

/// <summary>
/// Gets and sets promotion rules for type facets.
/// </summary>
public TypeFacetsPromotionRules TypeFacetsPromotionRules
{
get
{
return typeFacetsPromotionRules;
}

set
{
typeFacetsPromotionRules = value;
}
}

/// <summary>
/// Promote the left and right operand types
/// </summary>
Expand All @@ -49,7 +70,7 @@ public virtual void PromoteBinaryOperandTypes(
out IEdmTypeReference typeReference)
{
typeReference = null;
BinaryOperatorBinder.PromoteOperandTypes(binaryOperatorKind, ref leftNode, ref rightNode);
BinaryOperatorBinder.PromoteOperandTypes(binaryOperatorKind, ref leftNode, ref rightNode, typeFacetsPromotionRules);
}

/// <summary>
Expand Down
50 changes: 50 additions & 0 deletions src/Microsoft.OData.Core/UriParser/TypeFacetsPromotionRules.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//---------------------------------------------------------------------
// <copyright file="TypeFacetsPromotionRules.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

namespace Microsoft.OData.Core.UriParser
{
using System;

/// <summary>
/// Defines the promotion rules for type facets.
/// </summary>
public class TypeFacetsPromotionRules
{
/// <summary>
/// Computes the promoted precision value for left and right.
/// The default implementation works as follows:
/// 1) if both left and right are null, return null;
/// 2) if only one is null, return the other;
/// 3) otherwise, return the larger.
/// </summary>
/// <param name="left">Left-hand-side precision value.</param>
/// <param name="right">Right-hand-side precision value.</param>
/// <returns>The promoted precision value.</returns>
public virtual int? GetPromotedPrecision(int? left, int? right)
{
return left == null ? right :
right == null ? left :
Math.Max((int)left, (int)right);
}

/// <summary>
/// Computes the promoted scale value for left and right.
/// The default implementation works as follows:
/// 1) if both left and right are null, return null;
/// 2) if only one is null, return the other;
/// 3) otherwise, return the larger.
/// </summary>
/// <param name="left">Left-hand-side scale value.</param>
/// <param name="right">Right-hand-side scale value.</param>
/// <returns>The promoted scale value.</returns>
public virtual int? GetPromotedScale(int? left, int? right)
{
return left == null ? right :
right == null ? left :
Math.Max((int)left, (int)right);
}
}
}
Loading

0 comments on commit 63a66b6

Please sign in to comment.