Ejemplo n.º 1
0
        internal void ApplyDefaultBehaviour()
        {
            if (type.BaseType != null && model.FindWithoutAdd(type.BaseType) == null &&
                GetContractFamily(type.BaseType, null) != MetaType.AttributeFamily.None)
            {
                model.FindOrAddAuto(type.BaseType, true, false, false);
            }

            object[]        typeAttribs = type.GetCustomAttributes(true);
            AttributeFamily family      = GetContractFamily(type, typeAttribs);
            bool            isEnum      = type.IsEnum;

            if (family == AttributeFamily.None && !isEnum)
            {
                return;                                            // and you'd like me to do what, exactly?
            }
            BasicList      partialIgnores = null, partialMembers = null;
            int            dataMemberOffset = 0, implicitFirstTag = 1;
            bool           inferTagByName = model.InferTagFromNameDefault;
            ImplicitFields implicitMode   = ImplicitFields.None;

            for (int i = 0; i < typeAttribs.Length; i++)
            {
                Attribute item = (Attribute)typeAttribs[i];
                if (!isEnum && item is ProtoIncludeAttribute)
                {
                    ProtoIncludeAttribute pia = (ProtoIncludeAttribute)item;
                    AddSubType(pia.Tag, pia.KnownType);
                }
                if (item is ProtoPartialIgnoreAttribute)
                {
                    if (partialIgnores == null)
                    {
                        partialIgnores = new BasicList();
                    }
                    partialIgnores.Add(((ProtoPartialIgnoreAttribute)item).MemberName);
                }
                if (!isEnum && item is ProtoPartialMemberAttribute)
                {
                    if (partialMembers == null)
                    {
                        partialMembers = new BasicList();
                    }
                    partialMembers.Add(item);
                }
                if (!isEnum && item is ProtoContractAttribute)
                {
                    ProtoContractAttribute pca = (ProtoContractAttribute)item;
                    dataMemberOffset = pca.DataMemberOffset;
                    if (pca.InferTagFromNameHasValue)
                    {
                        inferTagByName = pca.InferTagFromName;
                    }
                    implicitMode   = pca.ImplicitFields;
                    UseConstructor = !pca.SkipConstructor;
                    if (pca.ImplicitFirstTag > 0)
                    {
                        implicitFirstTag = pca.ImplicitFirstTag;
                    }
                }
            }
            if (implicitMode != ImplicitFields.None)
            {
                family &= AttributeFamily.ProtoBuf; // with implicit fields, **only** proto attributes are important
            }
            MethodInfo[] callbacks = null;

            BasicList members = new BasicList();

            foreach (MemberInfo member in type.GetMembers(isEnum ? BindingFlags.Public | BindingFlags.Static
                : BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                if (member.DeclaringType != type)
                {
                    continue;
                }
                if (member.IsDefined(typeof(ProtoIgnoreAttribute), true))
                {
                    continue;
                }
                if (partialIgnores != null && partialIgnores.Contains(member.Name))
                {
                    continue;
                }

                bool forced = false, isPublic, isField;
                Type effectiveType;
                switch (member.MemberType)
                {
                case MemberTypes.Property:
                    PropertyInfo property = (PropertyInfo)member;
                    effectiveType = property.PropertyType;
                    isPublic      = property.GetGetMethod(false) != null;
                    isField       = false;
                    goto ProcessMember;

                case MemberTypes.Field:
                    FieldInfo field = (FieldInfo)member;
                    effectiveType = field.FieldType;
                    isPublic      = field.IsPublic;
                    isField       = true;
ProcessMember:
                    switch (implicitMode)
                    {
                    case ImplicitFields.AllFields:
                        if (isField)
                        {
                            forced = true;
                        }
                        break;

                    case ImplicitFields.AllPublic:
                        if (isPublic)
                        {
                            forced = true;
                        }
                        break;
                    }
                    if (effectiveType.IsSubclassOf(typeof(Delegate)))
                    {
                        continue;                                                   // we just don't like delegate types ;p
                    }
                    ProtoMemberAttribute normalizedAttribute = NormalizeProtoMember(member, family, forced, isEnum, partialMembers, dataMemberOffset, inferTagByName);
                    if (normalizedAttribute != null)
                    {
                        members.Add(normalizedAttribute);
                    }
                    break;

                case MemberTypes.Method:
                    if (isEnum)
                    {
                        continue;
                    }
                    MethodInfo method        = (MethodInfo)member;
                    object[]   memberAttribs = Attribute.GetCustomAttributes(method);
                    if (memberAttribs != null && memberAttribs.Length > 0)
                    {
                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoBeforeSerializationAttribute", ref callbacks, 0);
                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoAfterSerializationAttribute", ref callbacks, 1);
                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoBeforeDeserializationAttribute", ref callbacks, 2);
                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoAfterDeserializationAttribute", ref callbacks, 3);
                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnSerializingAttribute", ref callbacks, 4);
                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnSerializedAttribute", ref callbacks, 5);
                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnDeserializingAttribute", ref callbacks, 6);
                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnDeserializedAttribute", ref callbacks, 7);
                    }
                    break;
                }
            }
            ProtoMemberAttribute[] arr = new ProtoMemberAttribute[members.Count];
            members.CopyTo(arr, 0);

            if (inferTagByName || implicitMode != ImplicitFields.None)
            {
                Array.Sort(arr);
                int nextTag = implicitFirstTag;
                foreach (ProtoMemberAttribute normalizedAttribute in arr)
                {
                    if (!normalizedAttribute.TagIsPinned) // if ProtoMember etc sets a tag, we'll trust it
                    {
                        normalizedAttribute.Rebase(nextTag++);
                    }
                }
            }

            foreach (ProtoMemberAttribute normalizedAttribute in arr)
            {
                ValueMember vm = ApplyDefaultBehaviour(isEnum, normalizedAttribute);
                if (vm != null)
                {
                    Add(vm);
                }
            }

            if (callbacks != null)
            {
                SetCallbacks(Coalesce(callbacks, 0, 4), Coalesce(callbacks, 1, 5),
                             Coalesce(callbacks, 2, 6), Coalesce(callbacks, 3, 7));
            }
        }
 /// <summary>
 /// Apply include mappings via assembly reflection (<see cref="ProtoBaseAttribute"/>).<br />
 /// Important: This must be called before any serialization happen.
 /// </summary>
 /// <param name="model">The RuntimeTypeModel</param>
 /// <param name="assemblyRegexFilter">The regex assembly filer</param>
 /// <param name="implicitFields">The default import of base type fields, if nothing is specified.</param>
 /// <exception cref="ArgumentException"></exception>
 /// <exception cref="ArgumentNullException"></exception>
 /// <exception cref="InvalidDataException"></exception>
 public static void ApplyProtoBase(this RuntimeTypeModel model, Regex assemblyRegexFilter, ImplicitFields implicitFields = ImplicitFields.AllPublic)
 {
     model.ApplyProtoBase(assembly =>
                          assemblyRegexFilter == null || assemblyRegexFilter.IsMatch(assembly.FullName), implicitFields);
 }
        /// <summary>
        /// Apply include mappings via assembly reflection (<see cref="ProtoBaseAttribute"/>).<br />
        /// Important: This must be called before any serialization happen.
        /// </summary>
        /// <param name="model">The RuntimeTypeModel</param>
        /// <param name="assemblyFilter">The custom assembly filter</param>
        /// <param name="implicitFields">The default import of base type fields, if nothing is specified.</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        public static void ApplyProtoBase(this RuntimeTypeModel model, Func <Assembly, bool> assemblyFilter, ImplicitFields implicitFields = ImplicitFields.AllPublic)
        {
            AppDomain.CurrentDomain.GetAssemblies().Where(a => assemblyFilter?.Invoke(a) != false)
            .AsParallel().SelectMany(a =>
                                     a.GetTypes().Where(t => t.IsClass && t.GetCustomAttributes <ProtoBaseAttribute>(false).Any()))
            .ForAll(t =>
            {
                foreach (var attr in t.GetCustomAttributes <ProtoBaseAttribute>(false))
                {
                    if (attr.ConcreteType != null)
                    {
                        if (t.IsGenericType)
                        {
                            if (!attr.ConcreteType.IsGenericType ||
                                attr.ConcreteType.GetGenericTypeDefinition() != t)
                            {
                                throw new ArgumentException("Concrete type must be assignable from this type");
                            }
                        }
                        else if (!t.IsAssignableFrom(attr.ConcreteType))
                        {
                            throw new ArgumentException("Concrete type must be assignable from this type");
                        }
                    }

                    var type = attr.ConcreteType ?? t;

                    if (type.BaseType == null)
                    {
                        throw new ArgumentException("BaseType cannot be null");
                    }

                    if (type.IsGenericType && type.GenericTypeArguments.Length == 0)
                    {
                        throw new InvalidDataException($"Cannot add generic type without concrete definition. '{type.FullName}'");
                    }

                    Type baseType;
                    if (attr.InterfaceType != null)
                    {
                        if (!attr.InterfaceType.IsInterface)
                        {
                            throw new ArgumentException($"The attributed InterfaceType must be an interface. '{attr.InterfaceType.FullName}'");
                        }

                        if (!attr.InterfaceType.IsAssignableFrom(type))
                        {
                            throw new ArgumentException(
                                $"BaseType '{attr.InterfaceType.FullName}' must be assignable from type {type.FullName}.");
                        }

                        baseType = attr.InterfaceType;
                    }
                    else
                    {
                        if (type.BaseType == typeof(object))
                        {
                            baseType = type.GetInterfaces().FirstOrDefault();
                            if (baseType == null)
                            {
                                throw new ArgumentException($"BaseType cannot be of type '{typeof(object).FullName}' and no interfaces are defined.");
                            }
                        }
                        else
                        {
                            baseType = type.BaseType;
                        }
                    }

                    var metaType = model.Add(type, true);
                    if (metaType.BaseType != null && metaType.BaseType.Type != baseType)
                    {
                        throw new InvalidDataException(
                            $"Type '{type.FullName}' has already an assigned base type '{metaType.BaseType.Type.FullName}'");
                    }

                    MetaType baseMetaType;
                    if (baseType.IsClass &&
                        baseType.GetCustomAttribute <DataContractAttribute>() == null &&
                        baseType.GetCustomAttribute <ProtoContractAttribute>() == null &&
                        model.GetTypes().OfType <MetaType>().All(a => a.Type != baseType))
                    {
                        baseMetaType = model.Add(baseType, true);

                        void ApplyImplicitFields(BindingFlags bindingFlags)
                        {
                            var fields = baseType
                                         .GetProperties(bindingFlags | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                         .Cast <MemberInfo>()
                                         .Concat(baseType.GetFields(
                                                     bindingFlags | BindingFlags.Instance | BindingFlags.DeclaredOnly)).ToList();

                            if (fields.Any(a =>
                                           a.GetCustomAttribute <DataMemberAttribute>() != null ||
                                           a.GetCustomAttribute <ProtoMemberAttribute>() != null))
                            {
                                return;
                            }

                            var fieldNumber = 1;
                            foreach (var name in fields.Where(x =>
                                                              x.GetCustomAttribute <IgnoreDataMemberAttribute>() == null &&
                                                              x.GetCustomAttribute <ProtoIgnoreAttribute>() == null &&
                                                              x.GetCustomAttribute <CompilerGeneratedAttribute>() == null)
                                     .Select(a => a.Name))
                            {
                                baseMetaType.Add(fieldNumber++, name);
                            }
                        }

                        switch (implicitFields)
                        {
                        case ImplicitFields.AllPublic:
                            ApplyImplicitFields(BindingFlags.Public);
                            break;

                        case ImplicitFields.AllFields:
                            ApplyImplicitFields(BindingFlags.Public | BindingFlags.NonPublic);
                            break;

                        case ImplicitFields.None:
                            // Do noting
                            break;
                        }
                    }
                    else
                    {
                        baseMetaType = model.Add(baseType, true);
                    }

                    var rootMetaType = baseMetaType;
                    while (rootMetaType.BaseType != null)
                    {
                        rootMetaType = rootMetaType.BaseType;
                    }

                    void ValidateMetaTypes(MetaType mt)
                    {
                        if (mt.HasSubtypes)
                        {
                            if (mt.GetSubtypes().Any(a => a.FieldNumber == attr.FieldNumber))
                            {
                                throw new InvalidDataException(
                                    $"Cannot assign field number '{attr.FieldNumber}' for '{type.FullName}' on '{baseType.FullName}'. It's already being assigned to '{mt.Type.FullName}' in the hierarchy.");
                            }

                            foreach (var en in mt.GetSubtypes())
                            {
                                ValidateMetaTypes(en.DerivedType);
                            }
                        }
                    }

                    ValidateMetaTypes(rootMetaType);
                    baseMetaType.AddSubType(attr.FieldNumber, type);
                }
            });
        }
 /// <summary>
 /// Apply include mappings via assembly reflection (<see cref="ProtoBaseAttribute"/>).<br />
 /// Important: This must be called before any serialization happen.
 /// </summary>
 /// <param name="model">The RuntimeTypeModel</param>
 /// <param name="implicitFields">The default import of base type fields, if nothing is specified.</param>
 /// <exception cref="ArgumentException"></exception>
 /// <exception cref="InvalidDataException"></exception>
 public static void ApplyProtoBase(this RuntimeTypeModel model, ImplicitFields implicitFields = ImplicitFields.AllPublic)
 {
     model.ApplyProtoBase(assembly => true, implicitFields);
 }