Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Base term for IEdmTerm #1962

Draft
wants to merge 2 commits into
base: release-7.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Microsoft.OData.Edm/Csdl/CsdlConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static class CsdlConstants
internal const string Attribute_DateTimeOffset = "DateTimeOffset";
internal const string Attribute_Decimal = "Decimal";
internal const string Attribute_DefaultValue = "DefaultValue";
internal const string Attribute_BaseTerm = "BaseTerm";
internal const string Attribute_ElementType = "ElementType";
internal const string Attribute_Extends = "Extends";
internal const string Attribute_EntityType = "EntityType";
Expand Down
9 changes: 8 additions & 1 deletion src/Microsoft.OData.Edm/Csdl/Parsing/Ast/CsdlTerm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ internal class CsdlTerm : CsdlNamedElement
private readonly CsdlTypeReference type;
private readonly string appliesTo;
private readonly string defaultValue;
private readonly string baseTermName;

public CsdlTerm(string name, CsdlTypeReference type, string appliesTo, string defaultValue, CsdlLocation location)
public CsdlTerm(string name, CsdlTypeReference type, string baseTermName, string appliesTo, string defaultValue, CsdlLocation location)
: base(name, location)
{
this.type = type;
this.baseTermName = baseTermName;
this.appliesTo = appliesTo;
this.defaultValue = defaultValue;
}
Expand All @@ -28,6 +30,11 @@ public CsdlTypeReference Type
get { return this.type; }
}

public string BaseTermName
{
get { return this.baseTermName; }
}

public string AppliesTo
{
get { return this.appliesTo; }
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OData.Edm/Csdl/Parsing/CsdlDocumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,9 @@ private CsdlTerm OnTermElement(XmlElementInfo element, XmlElementValueCollection
string name = Required(CsdlConstants.Attribute_Name);
string appliesTo = Optional(CsdlConstants.Attribute_AppliesTo);
string defaultValue = Optional(CsdlConstants.Attribute_DefaultValue);
string baseTerm = Optional(CsdlConstants.Attribute_BaseTerm);

return new CsdlTerm(name, type, appliesTo, defaultValue, element.Location);
return new CsdlTerm(name, type, baseTerm, appliesTo, defaultValue, element.Location);
}

private CsdlAnnotations OnAnnotationsElement(XmlElementInfo element, XmlElementValueCollection childValues)
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.OData.Edm/Csdl/Parsing/SchemaJsonParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@ internal static CsdlTerm ParseCsdlTermType(string name, JsonElement element, Jso

IList<string> appliesTo = null;
string defaultValue = null;
string baseTerm = null;
IList<CsdlAnnotation> termAnnotations = new List<CsdlAnnotation>();
element.ParseAsObject(context, (propertyName, propertyValue) =>
{
Expand All @@ -1103,8 +1104,7 @@ internal static CsdlTerm ParseCsdlTermType(string name, JsonElement element, Jso

case "$BaseTerm":
// The value of $BaseTerm is the qualified name of the base term.
// Skip it because it's not supported
context.ReportError(EdmErrorCode.UnexpectedElement, Strings.CsdlJsonParser_UnexpectedJsonMember(context.Path, element.ValueKind));
baseTerm = propertyValue.ParseAsString(context);
break;

case "$DefaultValue":
Expand All @@ -1126,7 +1126,7 @@ internal static CsdlTerm ParseCsdlTermType(string name, JsonElement element, Jso
});

string appliesToStr = appliesTo != null ? string.Join(" ", appliesTo) : null;
CsdlTerm termType = new CsdlTerm(name, typeReference, appliesToStr, defaultValue, context.Location());
CsdlTerm termType = new CsdlTerm(name, typeReference, baseTerm, appliesToStr, defaultValue, context.Location());
termAnnotations.ForEach(a => termType.AddAnnotation(a));

return termType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public IEdmTypeReference Type
get { return this.type; }
}

public IEdmTerm BaseTerm
{
get { return null; }
}

public string AppliesTo
{
get { return this.appliesTo; }
Expand Down
34 changes: 34 additions & 0 deletions src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsTerm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ internal class CsdlSemanticsTerm : CsdlSemanticsElement, IEdmTerm, IEdmFullNamed

private readonly Cache<CsdlSemanticsTerm, IEdmTypeReference> typeCache = new Cache<CsdlSemanticsTerm, IEdmTypeReference>();
private static readonly Func<CsdlSemanticsTerm, IEdmTypeReference> ComputeTypeFunc = (me) => me.ComputeType();
private readonly Cache<CsdlSemanticsTerm, IEdmTerm> baseTermCache = new Cache<CsdlSemanticsTerm, IEdmTerm>();
private static readonly Func<CsdlSemanticsTerm, IEdmTerm> ComputeBaseTermFunc = (me) => me.ComputeBaseTerm();
private static readonly Func<CsdlSemanticsTerm, IEdmTerm> OnCycleBaseTermFunc = (me) => new CyclicTerm(me.GetCyclicBaseTermName(me.term.BaseTermName), me.Location);

public CsdlSemanticsTerm(CsdlSemanticsSchema context, CsdlTerm valueTerm)
: base(valueTerm)
Expand Down Expand Up @@ -61,6 +64,11 @@ public IEdmTypeReference Type
get { return this.typeCache.GetValue(this, ComputeTypeFunc, null); }
}

public IEdmTerm BaseTerm
{
get { return this.baseTermCache.GetValue(this, ComputeBaseTermFunc, OnCycleBaseTermFunc); }
}

public string AppliesTo
{
get { return this.term.AppliesTo; }
Expand Down Expand Up @@ -90,5 +98,31 @@ private IEdmTypeReference ComputeType()
{
return CsdlSemanticsModel.WrapTypeReference(this.Context, this.term.Type);
}

private IEdmTerm ComputeBaseTerm()
{
if (this.term.BaseTermName != null)
{
IEdmTerm baseTerm = this.Context.FindTerm(this.term.BaseTermName);
if (baseTerm != null)
{
// Evaluate the inductive step to detect cycles.
// Overriding BaseTerm getter from concrete type implementing IEdmTerm will be invoked to
// detect cycles. The object assignment is required by compiler only.
IEdmTerm baseTerm2 = baseTerm.BaseTerm;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar, but does this mean that we expect baseTerm.BaseTerm to throw or something if there is a cycle?

}

return baseTerm ?? new UnresolvedVocabularyTerm(this.term.BaseTermName);
}

return null;
}

// Resolves the real name of the base term, in case it was using an alias before.
protected string GetCyclicBaseTermName(string baseTermName)
{
IEdmTerm schemaBaseTerm = this.Context.FindTerm(baseTermName);
return (schemaBaseTerm != null) ? schemaBaseTerm.FullName() : baseTermName;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: I prefer the null propagation syntax here: return schemaBaseTerm?.FullName() ?? baseTermName

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ internal override void WriteTermElementHeader(IEdmTerm term, bool inlineType)
}

// A term MAY specialize another term in scope by specifying it as its base term.
// The value of $BaseTerm is the qualified name of the base term. So far, it's not supported.
// The value of $BaseTerm is the qualified name of the base term.
this.jsonWriter.WriteOptionalProperty("$BaseTerm", term.BaseTerm, this.SerializationName);

// It MAY contain the members $AppliesTo.
// The value of $AppliesTo is an array whose items are strings containing symbolic values from a table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ internal override void WriteTermElementHeader(IEdmTerm term, bool inlineType)
this.WriteRequiredAttribute(CsdlConstants.Attribute_Type, term.Type, this.TypeReferenceAsXml);
}

this.WriteOptionalAttribute(CsdlConstants.Attribute_BaseTerm, term.BaseTerm, this.SerializationName);

this.WriteOptionalAttribute(CsdlConstants.Attribute_DefaultValue, term.DefaultValue, EdmValueWriter.StringAsXml);
this.WriteOptionalAttribute(CsdlConstants.Attribute_AppliesTo, term.AppliesTo, EdmValueWriter.StringAsXml);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,15 @@ protected override void ProcessComplexType(IEdmComplexType element)
}
}

protected override void ProcessTerm(IEdmTerm element)
{
base.ProcessTerm(element);
if (element.BaseTerm != null)
{
this.CheckSchemaElementReference(element.BaseTerm);
}
}

protected override void ProcessEnumType(IEdmEnumType element)
{
base.ProcessEnumType(element);
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.OData.Edm/Microsoft.OData.Edm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ internal sealed class EdmRes {
internal const string Bad_CyclicComplex = "Bad_CyclicComplex";
internal const string Bad_CyclicEntityContainer = "Bad_CyclicEntityContainer";
internal const string Bad_UnresolvedNavigationPropertyPath = "Bad_UnresolvedNavigationPropertyPath";
internal const string Bad_CyclicTerm = "Bad_CyclicTerm";
internal const string RuleSet_DuplicateRulesExistInRuleSet = "RuleSet_DuplicateRulesExistInRuleSet";
internal const string EdmToClr_UnsupportedType = "EdmToClr_UnsupportedType";
internal const string EdmToClr_StructuredValueMappedToNonClass = "EdmToClr_StructuredValueMappedToNonClass";
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.OData.Edm/Microsoft.OData.Edm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ Bad_CyclicEntity=The entity '{0}' is invalid because its base type is cyclic.
Bad_CyclicComplex=The complex type '{0}' is invalid because its base type is cyclic.
Bad_CyclicEntityContainer=The entity container '{0}' is invalid because its extends hierarchy is cyclic.
Bad_UnresolvedNavigationPropertyPath=A navigation property could not be found for the path '{0}' starting from the type '{1}'.
Bad_CyclicTerm=The term '{0}' is invalid because its base type is cyclic.

; Error messages for validation rulesets
RuleSet_DuplicateRulesExistInRuleSet=The same rule cannot be in the same rule set twice.
Expand Down
45 changes: 32 additions & 13 deletions src/Microsoft.OData.Edm/Parameterized.Microsoft.OData.Edm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1583,15 +1583,15 @@ internal static string Serializer_SingleFileExpected
/// A string like "Unknown Edm version '{0}'."
/// </summary>
internal static string Serializer_UnknownEdmVersion(object p0)
{
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.Serializer_UnknownEdmVersion, p0);
}

/// <summary>
/// A string like "Unknown Edmx version '{0}'."
/// </summary>
internal static string Serializer_UnknownEdmxVersion(object p0)
{
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.Serializer_UnknownEdmxVersion, p0);
}

Expand Down Expand Up @@ -1955,77 +1955,88 @@ internal static string CsdlSemantics_DuplicateAlias(object p0, object p1)
/// <summary>
/// A string like "An unexpected '{0}' value kind was found when parsing the JSON path '{1}'. A '{2}' value kind was expected."
/// </summary>
internal static string CsdlJsonParser_UnexpectedJsonValueKind(object p0, object p1, object p2) {
internal static string CsdlJsonParser_UnexpectedJsonValueKind(object p0, object p1, object p2)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_UnexpectedJsonValueKind, p0, p1, p2);
}

/// <summary>
/// A string like "A member '{0}' is missing when parsing the JSON path '{1}'."
/// </summary>
internal static string CsdlJsonParser_MissingMemberInObject(object p0, object p1) {
internal static string CsdlJsonParser_MissingMemberInObject(object p0, object p1)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_MissingMemberInObject, p0, p1);
}

/// <summary>
/// A string like "A member '{0}' with value type '{1}' is unexpected."
/// </summary>
internal static string CsdlJsonParser_UnexpectedJsonMember(object p0, object p1) {
internal static string CsdlJsonParser_UnexpectedJsonMember(object p0, object p1)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_UnexpectedJsonMember, p0, p1);
}

/// <summary>
/// A string like "Cannot read the value '{0}' at JSON path '{1}' as '{2}' numeric value."
/// </summary>
internal static string CsdlJsonParser_CannotReadValueAsType(object p0, object p1, object p2) {
internal static string CsdlJsonParser_CannotReadValueAsType(object p0, object p1, object p2)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_CannotReadValueAsType, p0, p1, p2);
}

/// <summary>
/// A string like "A schema '{0}' object MUST contain the member '$Kind' with a string value of '{1}'."
/// </summary>
internal static string CsdlJsonParser_MissingKindMember(object p0, object p1) {
internal static string CsdlJsonParser_MissingKindMember(object p0, object p1)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_MissingKindMember, p0, p1);
}

/// <summary>
/// A string like "A property '{0}' is missing when parsing the JSON path '{1}'."
/// </summary>
internal static string CsdlJsonParser_MissingRequiredPropertyInObject(object p0, object p1) {
internal static string CsdlJsonParser_MissingRequiredPropertyInObject(object p0, object p1)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_MissingRequiredPropertyInObject, p0, p1);
}

/// <summary>
/// A string like "Found an unknown value kind '{0}' when parsing the JSON path '{1}'."
/// </summary>
internal static string CsdlJsonParser_UnknownJsonElementValueKind(object p0, object p1) {
internal static string CsdlJsonParser_UnknownJsonElementValueKind(object p0, object p1)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_UnknownJsonElementValueKind, p0, p1);
}

/// <summary>
/// A string like "Cannot parse a JSON number '{0}' when parsing the JSON path '{1}'."
/// </summary>
internal static string CsdlJsonParser_InvalidJsonNumberType(object p0, object p1) {
internal static string CsdlJsonParser_InvalidJsonNumberType(object p0, object p1)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_InvalidJsonNumberType, p0, p1);
}

/// <summary>
/// A string like "A member at JSON path '{0}' is not supported."
/// </summary>
internal static string CsdlJsonParser_UnsupportedJsonMember(object p0) {
internal static string CsdlJsonParser_UnsupportedJsonMember(object p0)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_UnsupportedJsonMember, p0);
}

/// <summary>
/// A string like "The version specified at '{0}' is not valid. It should be a string containing either '4.0' or '4.01'."
/// </summary>
internal static string CsdlJsonParser_InvalidCsdlVersion(object p0) {
internal static string CsdlJsonParser_InvalidCsdlVersion(object p0)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_InvalidCsdlVersion, p0);
}

/// <summary>
/// A string like "The schema object at '{0}' cannot have more than one entity container."
/// </summary>
internal static string CsdlJsonParser_SchemaCannotHaveMoreThanOneEntityContainer(object p0) {
internal static string CsdlJsonParser_SchemaCannotHaveMoreThanOneEntityContainer(object p0)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.CsdlJsonParser_SchemaCannotHaveMoreThanOneEntityContainer, p0);
}

Expand Down Expand Up @@ -2422,6 +2433,14 @@ internal static string Bad_UnresolvedNavigationPropertyPath(object p0, object p1
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.Bad_UnresolvedNavigationPropertyPath, p0, p1);
}

/// <summary>
/// A string like "The term '{0}' is invalid because its base type is cyclic."
/// </summary>
internal static string Bad_CyclicTerm(object p0)
{
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.Bad_CyclicTerm, p0);
}

/// <summary>
/// A string like "The same rule cannot be in the same rule set twice."
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.OData.Edm/Schema/AmbiguousTermBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public IEdmTypeReference Type
get { return this.type.GetValue(this, ComputeTypeFunc, null); }
}

public IEdmTerm BaseTerm
{
get { return null; }
}

public string AppliesTo
{
get { return this.appliesTo; }
Expand Down
22 changes: 22 additions & 0 deletions src/Microsoft.OData.Edm/Schema/CyclicTerm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//---------------------------------------------------------------------
// <copyright file="CyclicTerm.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

using Microsoft.OData.Edm.Validation;
using Microsoft.OData.Edm.Vocabularies;

namespace Microsoft.OData.Edm
{
/// <summary>
/// Represents an EDM term that cannot be determined due to a cyclic reference.
/// </summary>
internal class CyclicTerm : BadTerm
{
public CyclicTerm(string qualifiedName, EdmLocation location)
: base(qualifiedName, new EdmError[] { new EdmError(location, EdmErrorCode.BadCyclicTerm, Edm.Strings.Bad_CyclicTerm(qualifiedName)) })
{
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Add newline

5 changes: 5 additions & 0 deletions src/Microsoft.OData.Edm/Validation/EdmErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,11 @@ public enum EdmErrorCode
/// </summary>
AnnotationApplyToNotAllowedAnnotatable = 400,

/// <summary>
/// This ter, type is part of a cycle.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix docstring

/// </summary>
BadCyclicTerm,

/// <summary>
/// Invalid $Key value.
/// </summary>
Expand Down
Loading