Skip to content

Commit

Permalink
Fixes mamift#68 and add support for element default/fixed values
Browse files Browse the repository at this point in the history
  • Loading branch information
jods4 committed Jul 5, 2024
1 parent 85847c8 commit c9f408f
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 90 deletions.
14 changes: 3 additions & 11 deletions XObjectsCode/Src/ClrBasePropertyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public abstract class ClrBasePropertyInfo : ContentInfo

protected bool hasSet;
protected XCodeTypeReference returnType;
protected XCodeTypeReference defaultValueType;
protected bool isVirtual;
protected bool isOverride;

Expand All @@ -21,10 +20,9 @@ public abstract class ClrBasePropertyInfo : ContentInfo

public ClrBasePropertyInfo()
{
this.IsVirtual = false;
this.isOverride = false;
this.returnType = null;
this.defaultValueType = null;
IsVirtual = false;
isOverride = false;
returnType = null;
annotations = new List<ClrAnnotation>();
}

Expand Down Expand Up @@ -94,12 +92,6 @@ public virtual XCodeTypeReference ReturnType
set { returnType = value; }
}

public virtual XCodeTypeReference DefaultValueType
{
get { return defaultValueType; }
set { defaultValueType = value; }
}

public virtual string ClrTypeName
{
get { return null; }
Expand Down
137 changes: 68 additions & 69 deletions XObjectsCode/Src/ClrPropertyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public ClrPropertyInfo(string propertyName, string propertyNs, string schemaName
this.schemaName = schemaName;
this.hasSet = true;
this.returnType = null;
this.defaultValueType = null;
this.clrTypeName = null;
this.occursInSchema = occursInSchema;
if (this.occursInSchema > Occurs.ZeroOrOne)
Expand Down Expand Up @@ -282,9 +281,6 @@ public override bool VerifyRequired
public override XCodeTypeReference ReturnType
=> returnType ??= CreateReturnType(IsEnum ? typeRef.ClrFullTypeName : clrTypeName);

public override XCodeTypeReference DefaultValueType
=> defaultValueType ??= CreateReturnType(clrTypeName);

private string QualifiedType => typeRef.IsLocalType && !typeRef.IsSimpleType
? parentTypeFullName + "." + clrTypeName
: clrTypeName;
Expand Down Expand Up @@ -802,8 +798,6 @@ private void AddGetStatements(CodeStatementCollection getStatements)
return;
}

CodeExpression returnExp = null;

if (FixedValue != null)
{
getStatements.Add(
Expand All @@ -818,11 +812,13 @@ private void AddGetStatements(CodeStatementCollection getStatements)
getStatements.Add(GetValueMethodCall());
CheckOccurrence(getStatements);
CheckNillable(getStatements);
GetDefaultValue(getStatements);
CodeVariableReferenceExpression returnValueExp = new CodeVariableReferenceExpression("x");
CodeExpression returnExp;
if (!IsRef && typeRef.IsSimpleType)
{
//for referencing properties, directly create the object of referenced type
CodeTypeReference parseType = DefaultValueType;
CodeTypeReference parseType = ReturnType;
if (typeRef.IsValueType && IsNullable)
{
parseType = new CodeTypeReference(clrTypeName);
Expand Down Expand Up @@ -862,15 +858,7 @@ private void AddGetStatements(CodeStatementCollection getStatements)
returnValueExp,
simpleTypeExpression);

if (DefaultValue != null)
{
((CodeMethodInvokeExpression) returnExp).Parameters.Add(
new CodeFieldReferenceExpression(null,
NameGenerator.ChangeClrName(this.propertyName,
NameOptions.MakeDefaultValueField)));
}

if (this.IsEnum)
if (IsEnum)
{
// (EnumType) Enum.Parse(typeof(EnumType), returnExp)
returnExp = CodeDomHelper.CreateParseEnumCall(this.TypeReference.ClrFullTypeName, returnExp);
Expand All @@ -889,11 +877,25 @@ private void CheckOccurrence(CodeStatementCollection getStatements)
{
Debug.Assert(!this.IsList);
CodeStatement returnStatement = null;
if (CanBeAbsent && DefaultValue == null)
if (CanBeAbsent)
{
// For value types, this is needed to return T?, since ParseValue return T.
// It's not mandatory for ref types but it's more consistent and performant to do it always.
returnStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(null));
// Absent attributes return their default value (if any).
// Note that absent elements return null, only empty elements return their default value (per xsd specs).
if (DefaultValue != null && propertyOrigin == SchemaOrigin.Attribute)
{
returnStatement = new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(
null,
NameGenerator.ChangeClrName(propertyName, NameOptions.MakeDefaultValueField)
)
);
}
else
{
// For value types, this is needed to return T?, since ParseValue return T.
// It's not mandatory for ref types but it's more consistent and performant to do it always.
returnStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(null));
}
}
else if (VerifyRequired)
{
Expand Down Expand Up @@ -931,6 +933,30 @@ private void CheckNillable(CodeStatementCollection getStatements)
}
}

private void GetDefaultValue(CodeStatementCollection getStatements)
{
if (DefaultValue != null && propertyOrigin != SchemaOrigin.Attribute)
{
var x = new CodeVariableReferenceExpression("x");

getStatements.Add(
new CodeConditionStatement(
new CodeBinaryOperatorExpression(
new CodeBinaryOperatorExpression(x, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)),
CodeBinaryOperatorType.BooleanAnd,
new CodeFieldReferenceExpression(x, "IsEmpty")
),
new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(
null,
NameGenerator.ChangeClrName(propertyName, NameOptions.MakeDefaultValueField)
)
)
)
);
}
}

private void AddSetStatements(CodeStatementCollection setStatements)
{
AddFixedValueChecking(setStatements);
Expand Down Expand Up @@ -1149,36 +1175,29 @@ public void SetFixedDefaultValue(ClrWrapperTypeInfo typeInfo)

protected void CreateFixedDefaultValue(CodeTypeDeclaration typeDecl)
{
if (fixedDefaultValue != null)
{
//Add Fixed/Default value wrapping field
CodeMemberField fixedOrDefaultField = null;
CodeTypeReference returnType = DefaultValueType;
if (this.unionDefaultType != null)
{
returnType = new CodeTypeReference(unionDefaultType.ToString());
}
if (fixedDefaultValue == null) return;
// Add Fixed/Default value wrapping field

if (FixedValue != null)
{
fixedOrDefaultField = new CodeMemberField(returnType,
NameGenerator.ChangeClrName(PropertyName, NameOptions.MakeFixedValueField));
}
else // if (DefaultValue != null)
{
fixedOrDefaultField = new CodeMemberField(returnType,
NameGenerator.ChangeClrName(PropertyName, NameOptions.MakeDefaultValueField));
}
CodeTypeReference returnType = unionDefaultType != null
? new CodeTypeReference(unionDefaultType.ToString())
: ReturnType;

CodeDomHelper.AddBrowseNever(fixedOrDefaultField);
fixedOrDefaultField.Attributes = (fixedOrDefaultField.Attributes & ~MemberAttributes.AccessMask &
~MemberAttributes.ScopeMask)
| MemberAttributes.Private | MemberAttributes.Static;
var fieldName = NameGenerator.ChangeClrName(
PropertyName,
FixedValue != null ? NameOptions.MakeFixedValueField : NameOptions.MakeDefaultValueField /* DefaultValue != null */);

fixedOrDefaultField.InitExpression =
SimpleTypeCodeDomHelper.CreateFixedDefaultValueExpression(returnType, fixedDefaultValue);
typeDecl.Members.Add(fixedOrDefaultField);
}
var fixedOrDefaultField = new CodeMemberField(returnType, fieldName);
CodeDomHelper.AddBrowseNever(fixedOrDefaultField);

fixedOrDefaultField.Attributes =
(fixedOrDefaultField.Attributes & ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask)
| MemberAttributes.Private
| MemberAttributes.Static;

fixedOrDefaultField.InitExpression =
SimpleTypeCodeDomHelper.CreateFixedDefaultValueExpression(returnType, fixedDefaultValue, IsEnum);

typeDecl.Members.Add(fixedOrDefaultField);
}

protected void AddFixedValueChecking(CodeStatementCollection setStatements)
Expand All @@ -1191,9 +1210,9 @@ protected void AddFixedValueChecking(CodeStatementCollection setStatements)
setStatements.Add(
new CodeConditionStatement(
CodeDomHelper.CreateMethodCall(
new CodePropertySetValueReferenceExpression(),
fixedValueExpr,
Constants.EqualityCheck,
fixedValueExpr
new CodePropertySetValueReferenceExpression()
),
new CodeStatement[] { },
new CodeStatement[]
Expand All @@ -1207,25 +1226,5 @@ protected void AddFixedValueChecking(CodeStatementCollection setStatements)
);
}
}

public virtual void InsertDefaultFixedValueInDefaultCtor(CodeConstructor ctor)
{
if (this.FixedValue != null)
{
ctor.Statements.Add(
new CodeAssignStatement(
CodeDomHelper.CreateFieldReference(null, propertyName),
CodeDomHelper.CreateFieldReference(null,
NameGenerator.ChangeClrName(propertyName, NameOptions.MakeFixedValueField))));
}
else if (DefaultValue != null)
{
ctor.Statements.Add(
new CodeAssignStatement(
CodeDomHelper.CreateFieldReference(null, propertyName),
CodeDomHelper.CreateFieldReference(null,
NameGenerator.ChangeClrName(propertyName, NameOptions.MakeDefaultValueField))));
}
}
}
}
22 changes: 13 additions & 9 deletions XObjectsCode/Src/SimpleTypeCodeDomHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ private static CodeExpression CreateTypeConversionExpr(string typeName, object v
new CodePrimitiveExpression(value.ToString()));
}

internal static CodeExpression CreateValueExpression(string builtInType, string strValue)
internal static CodeExpression CreateValueExpression(string builtInType, string strValue, bool isEnum)
{
int dot = builtInType.LastIndexOf('.');
Debug.Assert(dot != -1);
Expand All @@ -416,6 +416,10 @@ internal static CodeExpression CreateValueExpression(string builtInType, string
{
return new CodePrimitiveExpression(strValue);
}
else if (isEnum)
{
return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(builtInType), strValue);
}
else if (localType == "Uri")
{
return new CodeObjectCreateExpression("Uri", new CodePrimitiveExpression(strValue));
Expand All @@ -426,41 +430,41 @@ internal static CodeExpression CreateValueExpression(string builtInType, string
}
}

internal static CodeArrayCreateExpression CreateFixedDefaultArrayValueInit(string baseType, string value)
internal static CodeArrayCreateExpression CreateFixedDefaultArrayValueInit(string baseType, string value, bool isEnum)
{
CodeArrayCreateExpression array = new CodeArrayCreateExpression(baseType);
var array = new CodeArrayCreateExpression(baseType);
foreach (string s in value.Split(' '))
{
array.Initializers.Add(CreateValueExpression(baseType, s));
array.Initializers.Add(CreateValueExpression(baseType, s, isEnum));
}

return array;
}

internal static CodeExpression CreateFixedDefaultValueExpression(CodeTypeReference type, string value)
internal static CodeExpression CreateFixedDefaultValueExpression(CodeTypeReference type, string value, bool isEnum)
{
string baseType = type.BaseType;
if (baseType.Contains("Nullable"))
{
Debug.Assert(type.TypeArguments.Count == 1);
baseType = type.TypeArguments[0].BaseType;
return CreateValueExpression(baseType, value);
return CreateValueExpression(baseType, value, isEnum);
}
else if (type.ArrayRank != 0)
{
baseType = type.ArrayElementType.BaseType;
return CreateFixedDefaultArrayValueInit(baseType, value);
return CreateFixedDefaultArrayValueInit(baseType, value, isEnum);
}
else if (baseType.Contains("List"))
{
//Create sth like: new List<string>(new string[] { });
Debug.Assert(type.TypeArguments.Count == 1);

baseType = type.TypeArguments[0].BaseType;
return CreateFixedDefaultArrayValueInit(baseType, value);
return CreateFixedDefaultArrayValueInit(baseType, value, isEnum);
}

return CreateValueExpression(baseType, value);
return CreateValueExpression(baseType, value, isEnum);
}
}
}
33 changes: 32 additions & 1 deletion XObjectsCode/Src/XsdToTypesConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ private ClrPropertyInfo BuildPropertyForElement(XmlSchemaElement elem, bool from
propertyInfo.ClrNamespace = clrNs;
propertyInfo.IsNillable = elem.IsNillable;

//SetFixedDefaultValue(elem, propertyInfo);
SetFixedDefaultValue(elem, propertyInfo);

if (substitutionMembers != null)
{
Expand Down Expand Up @@ -1191,5 +1191,36 @@ private void SetFixedDefaultValue(XmlSchemaAttribute attribute, ClrPropertyInfo
}
}
}

private void SetFixedDefaultValue(XmlSchemaElement element, ClrPropertyInfo propertyInfo)
{
//saves fixed/default value in the corresponding property
//Currently only consider fixed/default values for simple types
if (element.RefName != null && !element.RefName.IsEmpty)
{
var globalEl = (XmlSchemaElement)schemas.GlobalElements[element.RefName];
propertyInfo.FixedValue = globalEl.FixedValue;
propertyInfo.DefaultValue = globalEl.DefaultValue;
}
else
{
propertyInfo.FixedValue = element.FixedValue;
propertyInfo.DefaultValue = element.DefaultValue;
}

if (element.ElementSchemaType.DerivedBy == XmlSchemaDerivationMethod.Union)
{
string value = propertyInfo.FixedValue;
if (value == null)
value = propertyInfo.DefaultValue;
if (value != null)
{
propertyInfo.unionDefaultType = element
.ElementSchemaType.Datatype
.ParseValue(value, new NameTable(), null)
.GetType();
}
}
}
}
}
3 changes: 3 additions & 0 deletions XObjectsCore/API/XTypedServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,9 @@ public static T ParseValue<T>(XAttribute attribute, XmlSchemaDatatype datatype)
return ParseValue<T>(attribute.Value, attribute.Parent, datatype);
}

// Kept for backward compatibility with code generated in previous versions.
// Current generator does not use this method anymore, as attributes with default properties
// have `if (attr == null) return defaultValue` directly in getter to workaround enum parsing.
public static T ParseValue<T>(XAttribute attribute, XmlSchemaDatatype datatype, T defaultValue)
{
if (attribute == null)
Expand Down

0 comments on commit c9f408f

Please sign in to comment.