// property deserialization defaults

        /// <summary>
        /// Returns true if the given property should be deserialized.
        ///
        /// Override to tweak behavior.
        /// </summary>
        protected virtual bool ShouldDeserialize(TypeInfo forType, PropertyInfo property)
        {
            if (property.SetMethod == null)
            {
                return(false);
            }

            var ignoreDataMember = property.GetCustomAttribute <IgnoreDataMemberAttribute>();

            if (ignoreDataMember != null)
            {
                return(false);
            }

            var dataMember = property.GetCustomAttribute <DataMemberAttribute>();

            if (dataMember != null)
            {
                return(true);
            }

            return
                (property.SetMethod != null &&
                 property.SetMethod.IsPublic &&
                 !property.SetMethod.IsStatic &&
                 property.SetMethod.GetParameters().Length == 1 &&
                 DeserializableMember.GetDefaultParser(property.SetMethod.GetParameters()[0].ParameterType.GetTypeInfo()) != null);
        }
Beispiel #2
0
        private ManualTypeDescriberBuilder WithDeserializeMember(TypeInfo?forType, Setter?setter, string?name, Parser?parser, MemberRequired required, Reset?reset)
        {
            if (forType == null)
            {
                Throw.ArgumentNullException(nameof(forType));
            }

            if (setter == null)
            {
                Throw.ArgumentNullException(nameof(setter));
            }

            if (name == null)
            {
                Throw.ArgumentNullException(nameof(name));
            }

            if (parser == null)
            {
                Throw.ArgumentNullException(nameof(parser));
            }

            var toAdd = DeserializableMember.Create(forType, name, setter, parser, required, reset);

            if (!Deserializers.TryGetValue(forType, out var d))
            {
                Deserializers[forType] = d = ImmutableArray.CreateBuilder <DeserializableMember>();
            }

            d.Add(toAdd);

            return(this);
        }
        /// <summary>
        /// Enumerate all columns to deserialize.
        /// </summary>
        public virtual IEnumerable <DeserializableMember> EnumerateMembersToDeserialize(TypeInfo forType)
        {
            var buffer = new List <(DeserializableMember Member, int?Position)>();

            foreach (var p in forType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
            {
                if (!ShouldDeserialize(forType, p))
                {
                    continue;
                }

                var name       = GetDeserializationName(forType, p);
                var setter     = GetSetter(forType, p);
                var parser     = GetParser(forType, p);
                var order      = GetPosition(forType, p);
                var isRequired = GetIsRequired(forType, p);
                var reset      = GetReset(forType, p);

                buffer.Add((DeserializableMember.Create(forType, name, setter, parser, isRequired, reset), order));
            }

            foreach (var f in forType.GetFields())
            {
                if (!ShouldDeserialize(forType, f))
                {
                    continue;
                }

                var name       = GetDeserializationName(forType, f);
                var parser     = GetParser(forType, f);
                var order      = GetPosition(forType, f);
                var isRequired = GetIsRequired(forType, f);
                var reset      = GetReset(forType, f);

                buffer.Add((DeserializableMember.Create(forType, name, f, parser, isRequired, reset), order));
            }

            buffer.Sort(TypeDescribers.DeserializableComparer);

            foreach (var(member, _) in buffer)
            {
                yield return(member);
            }
        }
Beispiel #4
0
 /// <summary>
 /// Returns the default cell parsing method for the given type, if any.
 ///
 /// Override to tweak behavior.
 /// </summary>
 protected virtual MethodInfo GetCellParsingMethodFor(int columnNumber, string columName, TypeInfo targetType)
 => DeserializableMember.GetDefaultParser(targetType);
 private static MethodInfo GetParser(TypeInfo forType)
 => DeserializableMember.GetDefaultParser(forType);
        private static DeserializableMember Map(TypeInfo ontoType, DeserializableMember member)
        {
            MethodInfo?resetOnType = null;

            if (member.Reset.HasValue)
            {
                var surrogateResetWrapper = member.Reset.Value;
                if (surrogateResetWrapper.Mode != BackingMode.Method)
                {
                    Throw.InvalidOperationException($"Cannot map reset {surrogateResetWrapper} onto {ontoType}, reset isn't backed by a method");
                }

                var surrogateReset = surrogateResetWrapper.Method.Value;

                var surrogateResetBinding = GetEquivalentFlagsFor(surrogateReset.IsPublic, surrogateReset.IsStatic);

                Type[] resetTakesTypes;
                if (surrogateResetWrapper.TakesContext)
                {
                    resetTakesTypes = new[] { Types.ReadContext.MakeByRefType() };
                }
                else
                {
                    resetTakesTypes = Type.EmptyTypes;
                }

                // intentionally letting this be null
                resetOnType = ontoType.GetMethod(surrogateReset.Name, surrogateResetBinding, null, resetTakesTypes, null);
                if (resetOnType == null)
                {
                    Throw.InvalidOperationException($"No equivalent to {resetOnType} found on {ontoType}");
                }
            }

            var surrogateSetterWrapper = member.Setter;

            switch (surrogateSetterWrapper.Mode)
            {
            case BackingMode.Field:
            {
                var surrogateField        = surrogateSetterWrapper.Field.Value;
                var surrogateFieldBinding = GetEquivalentFlagsFor(surrogateField.IsPublic, surrogateField.IsStatic);

                // intentionally allowing null here
                var fieldOnType = ontoType.GetField(surrogateField.Name, surrogateFieldBinding);
                if (fieldOnType == null)
                {
                    Throw.InvalidOperationException($"No equivalent to {surrogateField} found on {ontoType}");
                }

                if (fieldOnType.FieldType != surrogateField.FieldType)
                {
                    Throw.InvalidOperationException($"Field {fieldOnType} type ({fieldOnType.FieldType}) does not match surrogate field {surrogateField} type ({surrogateField.FieldType})");
                }

                var required = GetEquivalentRequiredFor(member.IsRequired);

                return(DeserializableMember.CreateInner(ontoType, member.Name, (Setter?)fieldOnType, member.Parser, required, (Reset?)resetOnType, null));
            }

            case BackingMode.Method:
            {
                var surrogateSetter = surrogateSetterWrapper.Method.Value;

                var surrogateSetterBinding = GetEquivalentFlagsFor(surrogateSetter.IsPublic, surrogateSetter.IsStatic);

                // intentionally letting this be null
                var setterOnType = ontoType.GetMethod(surrogateSetter.Name, surrogateSetterBinding);
                if (setterOnType == null)
                {
                    Throw.InvalidOperationException($"No equivalent to {surrogateSetter} found on {ontoType}");
                }

                var paramsOnType      = setterOnType.GetParameters();
                var paramsOnSurrogate = surrogateSetter.GetParameters();

                if (paramsOnType.Length != paramsOnSurrogate.Length)
                {
                    Throw.InvalidOperationException($"Parameters for {setterOnType} do not match parameters for {surrogateSetter}");
                }

                for (var i = 0; i < paramsOnType.Length; i++)
                {
                    var pOnType      = paramsOnType[i];
                    var pOnSurrogate = paramsOnSurrogate[i];

                    if (pOnType.ParameterType != pOnSurrogate.ParameterType)
                    {
                        Throw.InvalidOperationException($"Parameter #{(i + 1)} on {setterOnType} does not match same parameter on {surrogateSetter}");
                    }
                }

                var required = GetEquivalentRequiredFor(member.IsRequired);

                return(DeserializableMember.CreateInner(ontoType, member.Name, (Setter?)setterOnType, member.Parser, required, (Reset?)resetOnType, null));
            }

            case BackingMode.Delegate:
                Throw.InvalidOperationException($"Cannot map setter {surrogateSetterWrapper} onto {ontoType}, setter is backed by a delegate");
                return(default);

            default:
                Throw.InvalidOperationException($"Unexpected {nameof(BackingMode)}: {surrogateSetterWrapper.Mode}");
                return(default);
            }
        }
Beispiel #7
0
        private static DeserializableMember Map(TypeInfo ontoType, DeserializableMember member)
        {
            MethodInfo resetOnType = null;

            if (member.Reset != null)
            {
                var surrogateReset        = member.Reset;
                var surrogateResetBinding =
                    // explicitly ignoring DeclaredOnly; shadowing is fine
                    (surrogateReset.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                    (surrogateReset.IsStatic ? BindingFlags.Static : BindingFlags.Instance);

                resetOnType = ontoType.GetMethod(surrogateReset.Name, surrogateResetBinding);
                if (resetOnType == null)
                {
                    Throw.InvalidOperationException($"No equivalent to {resetOnType} found on {ontoType}");
                }
            }

            if (member.Field != null)
            {
                var surrogateField        = member.Field;
                var surrogateFieldBinding =
                    // explicitly ignoring DeclaredOnly; shadowing is fine
                    (surrogateField.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                    (surrogateField.IsStatic ? BindingFlags.Static : BindingFlags.Instance);

                var fieldOnType = ontoType.GetField(surrogateField.Name, surrogateFieldBinding);
                if (fieldOnType == null)
                {
                    Throw.InvalidOperationException($"No equivalent to {surrogateField} found on {ontoType}");
                }

                if (fieldOnType.FieldType != surrogateField.FieldType)
                {
                    Throw.InvalidOperationException($"Field {fieldOnType} type ({fieldOnType.FieldType}) does not match surrogate field {surrogateField} type ({surrogateField.FieldType})");
                }
                return(DeserializableMember.Create(ontoType, member.Name, fieldOnType, member.Parser, member.IsRequired, resetOnType));
            }

            var surrogateSetter        = member.Setter;
            var surrogateSetterBinding =
                // explicitly ignoring DeclaredOnly; shadowing is fine
                (surrogateSetter.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                (surrogateSetter.IsStatic ? BindingFlags.Static : BindingFlags.Instance);

            var setterOnType = ontoType.GetMethod(surrogateSetter.Name, surrogateSetterBinding);

            if (setterOnType == null)
            {
                Throw.InvalidOperationException($"No equivalent to {surrogateSetter} found on {ontoType}");
            }

            var paramsOnType      = setterOnType.GetParameters();
            var paramsOnSurrogate = surrogateSetter.GetParameters();

            if (paramsOnType.Length != paramsOnSurrogate.Length)
            {
                Throw.InvalidOperationException($"Parameters for {setterOnType} do not match parameters for {surrogateSetter}");
            }

            for (var i = 0; i < paramsOnType.Length; i++)
            {
                var pOnType      = paramsOnType[i];
                var pOnSurrogate = paramsOnSurrogate[i];

                if (pOnType.ParameterType != pOnSurrogate.ParameterType)
                {
                    Throw.InvalidOperationException($"Parameter #{(i + 1)} on {setterOnType} does not match same parameter on {surrogateSetter}");
                }
            }

            return(DeserializableMember.Create(ontoType, member.Name, setterOnType, member.Parser, member.IsRequired, resetOnType));
        }
Beispiel #8
0
        /// <summary>
        /// Enumerate members which will be deserialized for the given type.
        ///
        /// Note that these members are generated ahead of time with a source gneerator,
        ///   and cannot be changed at runtime.
        /// </summary>
        public IEnumerable <DeserializableMember> EnumerateMembersToDeserialize(TypeInfo forType)
        {
            var paired = GetPairedType(forType, DESERIALIZER_KIND);

            if (paired == null)
            {
                return(Enumerable.Empty <DeserializableMember>());
            }

            var colNamesProp = paired.GetPropertyNonNull("__ColumnNames", PublicStatic);
            var colNames     = (ImmutableArray <string>)colNamesProp.GetValueNonNull(null);

            var ret = ImmutableArray.CreateBuilder <DeserializableMember>(colNames.Length);

            ParameterInfo[]? consPs = null;

            for (var i = 0; i < colNames.Length; i++)
            {
                var name = colNames[i];

                var colReaderName = $"__Column_{i}";
                var colReaderMtd  = paired.GetMethodNonNull(colReaderName, PublicInstance);

#pragma warning disable CS0618 // These are obsolete to prevent clients from using them, but they are fine for us.
                var isRequired = colReaderMtd.GetCustomAttribute <IsRequiredAttribute>() != null;
                var setterBackedByParameter = colReaderMtd.GetCustomAttribute <SetterBackedByConstructorParameterAttribute>();
                var setterIsInitOnly        = colReaderMtd.GetCustomAttribute <SetterBackedByInitOnlyPropertyAttribute>();
#pragma warning restore CS0618

                Setter setter;
                if (setterBackedByParameter == null && setterIsInitOnly == null)
                {
                    // directly a method
                    var setterName = $"__Column_{i}_Setter";
                    var setterMtd  = paired.GetMethodNonNull(setterName, PublicStatic);
                    setter = Setter.ForMethod(setterMtd);
                }
                else if (setterBackedByParameter != null)
                {
                    // parameter to constructor
                    if (consPs == null)
                    {
                        var ip = GetInstanceProvider(forType);
                        ip = Utils.NonNull(ip);

                        var cons = ip.Constructor.Value;
                        consPs = cons.GetParameters();
                    }

                    var consParameterIndex = setterBackedByParameter.Index;
                    if (consParameterIndex < 0 || consParameterIndex >= consPs.Length)
                    {
                        Throw.ImpossibleException($"Setter for column {i} claims to be backed by constructor parameter, but its position is out of bounds (index={consParameterIndex})");
                    }

                    var p = consPs[setterBackedByParameter.Index];
                    setter = Setter.ForConstructorParameter(p);
                }
                else
                {
                    // init only property
                    var initOnly = Utils.NonNull(setterIsInitOnly);

                    var prop = forType.GetProperty(initOnly.PropertyName, initOnly.BindingFlags);
                    if (prop == null)
                    {
                        Throw.ImpossibleException($"Setter for column {i} claims to be backed by init-only property {initOnly.PropertyName} with bindings ({initOnly.BindingFlags}), but it could not be found");
                    }

                    setter = Setter.ForProperty(prop);
                }

                var resetMethodName = $"__Column_{i}_Reset";
                var resetMtd        = paired.GetMethod(resetMethodName, PublicStatic);
                var reset           = (Reset?)resetMtd;

                var    parserMethodName = $"__Column_{i}_Parser";
                var    parserMtd        = paired.GetMethod(parserMethodName, PublicStatic);
                Parser?parser;
                if (parserMtd != null)
                {
                    parser = Parser.ForMethod(parserMtd);
                }
                else
                {
                    parser = Utils.NonNull(Parser.GetDefault(setter.Takes));
                }

                ret.Add(DeserializableMember.CreateInner(forType, name, setter, parser, isRequired ? MemberRequired.Yes : MemberRequired.No, reset, paired));
            }

            return(ret.ToImmutable());
        }