private static CodeExpression GetCreateExpression(TypeSafeDataEntry c, Type t) { if (t.IsPrimitive) { if (c.Parameters.Length == 0 || c.Parameters.Length > 1 || !c.Parameters[0].GetType().IsPrimitive || c.Parameters[0].GetType() != t) { throw new ArgumentException("Primitive DataType must have a single parameter of the same type"); } return(new CodePrimitiveExpression(c.Parameters[0])); } if (t == typeof(string)) { if (c.Parameters.Length == 0 || c.Parameters.Length > 1 || !(c.Parameters[0] is string)) { throw new ArgumentException("String DataType must have a single string parameter"); } return(new CodePrimitiveExpression(c.Parameters[0])); } if (!c.Parameters.All(p => p.GetType().IsPrimitive || p is string)) { throw new ArgumentException("Parameters must be all primitive types"); } var dataType = new CodeTypeReference(t); CompilerUtil.SetTypeGlobalReference(dataType); return(new CodeObjectCreateExpression(dataType, c.Parameters.Select(p => new CodePrimitiveExpression(p)).Cast <CodeExpression>().ToArray())); }
private static void PopulateClassMembers(TypeSafeDataUnit unit, CodeTypeDeclaration container) { // Create the list type that is used to store the internal array (not used when All list is disabled) var valueListType = new CodeTypeReference(typeof(IList <>)) { TypeArguments = { unit.DataType } }; // Set up the default type for members var dataType = new CodeTypeReference(unit.DataType); // Use global references CompilerUtil.SetTypeGlobalReference(dataType); CompilerUtil.SetTypeGlobalReference(valueListType); // Expression to make the internal list (not used when All list is disabled) var arrayExpression = new CodeArrayCreateExpression(dataType); //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Populating {container.Name}"); foreach (var c in unit.Data) { // Can't override type if using the All property method (since can't store non-compatible types in the list) if (unit.EnableAllProperty && c.OverrideType != null) { throw new InvalidOperationException("Cannot override type when using All list."); } var name = c.PropertyName; string memberName; if ( !CompilerUtil.GetSafeNameAndVerifyNotDuplicate(name, container, out memberName, !c.OverrideRestrictedNames)) { continue; } // Create the expression to initialize this member var entryExpression = GetCreateExpression(c, c.OverrideType != null ? c.OverrideType : unit.DataType); var isObsolete = !string.IsNullOrEmpty(c.ObsoleteWarning); //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Member (name={memberName}, isObsolete={isObsolete})"); // Exclude obsolete members from the _all array if (unit.EnableAllProperty && !isObsolete) { // Add the initializer expression to the internal _all array arrayExpression.Initializers.Add(entryExpression); } CodeTypeMember member; // Create a field if one of the following criteria matches: // - Entry is a primitive or string. We duplicate the data (include in _all array and const field) so that const uses are faster // - All property is disabled, so we don't have an internal _all array to access // - Entry has an obsolete warning and so isn't included in the internal _all array. if (!unit.EnableAllProperty || unit.DataType.IsPrimitive || unit.DataType == typeof(string) || isObsolete) { //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Member handled as field"); var entryTypeReference = dataType; var attributes = MemberAttributes.Public; if (c.OverrideType != null) { entryTypeReference = new CodeTypeReference(c.OverrideType); } if (CompilerUtil.IsPrimitiveType(unit.DataType)) { attributes |= MemberAttributes.Const; } else { attributes |= MemberAttributes.Static; entryTypeReference = new CodeTypeReference("readonly global::" + entryTypeReference.BaseType); } // Duplicate data and create a field for data entry member = new CodeMemberField { Name = memberName, Type = entryTypeReference, Attributes = attributes, InitExpression = entryExpression }; if (isObsolete) { //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Adding obsolete warning: {c.ObsoleteWarning}"); member.CustomAttributes.Add(CompilerUtil.GetObsoleteAttribute(c.ObsoleteWarning, false)); } } else { //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Member handled as property"); // Otherwise create a property getter to access the internal _all array element member = new CodeMemberProperty { Name = memberName, Type = dataType, HasSet = false, HasGet = true, Attributes = MemberAttributes.Static | MemberAttributes.Public, GetStatements = { new CodeMethodReturnStatement(new CodeArrayIndexerExpression( new CodeVariableReferenceExpression("__all"), new CodePrimitiveExpression(arrayExpression.Initializers.Count - 1))) } }; } container.Members.Add(member); } if (unit.EnableAllProperty) { var all = new CodeMemberField(valueListType, "__all") { InitExpression = new CodeObjectCreateExpression( new CodeTypeReference(typeof(ReadOnlyCollection <>), CodeTypeReferenceOptions.GlobalReference) { TypeArguments = { dataType } }, arrayExpression), Attributes = MemberAttributes.Private | MemberAttributes.Static }; container.Members.Add(all); var allPublic = new CodeMemberProperty { Name = "All", Type = valueListType, GetStatements = { new CodeMethodReturnStatement(new CodeFieldReferenceExpression { FieldName = "__all" }) }, HasGet = true, Attributes = MemberAttributes.Public | MemberAttributes.Static }; container.Members.Add(allPublic); } }