private static void TestEnumerableListPatterns(TypeModel model, BasicList candidates, Type iType) { #if WINRT TypeInfo iTypeInfo = iType.GetTypeInfo(); if (iTypeInfo.IsGenericType) { Type typeDef = iTypeInfo.GetGenericTypeDefinition(); if (typeDef == typeof(System.Collections.Generic.ICollection <>) || typeDef.GetTypeInfo().FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1") { Type[] iTypeArgs = iTypeInfo.GenericTypeArguments; if (!candidates.Contains(iTypeArgs[0])) { candidates.Add(iTypeArgs[0]); } } } #elif !NO_GENERICS if (iType.IsGenericType) { Type typeDef = iType.GetGenericTypeDefinition(); if (typeDef == model.MapType(typeof(System.Collections.Generic.IEnumerable <>)) || typeDef == model.MapType(typeof(System.Collections.Generic.ICollection <>)) || typeDef.FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1") { Type[] iTypeArgs = iType.GetGenericArguments(); if (!candidates.Contains(iTypeArgs[0])) { candidates.Add(iTypeArgs[0]); } } } #endif }
void CscadeDependents_Member(BasicList list, Type type) { WireType defaultWireType; IProtoSerializer coreSerializer = this.ValueSerializerBuilder.TryGetSimpleCoreSerializer(BinaryDataFormat.Default, type, out defaultWireType); if (coreSerializer == null) { int index = FindOrAddAuto(type, false, false, false); if (index >= 0) { var tmp = ((MetaType)_types[index]).GetSurrogateOrBaseOrSelf(false); if (!list.Contains(tmp)) { // could perhaps also implement as a queue, but this should work OK for sane models list.Add(tmp); CascadeDependents(list, tmp); } } } }
internal static Type GetListItemType(TypeModel model, Type listType) { Helpers.DebugAssert(listType != null); #if WINRT TypeInfo listTypeInfo = listType.GetTypeInfo(); if (listType == typeof(string) || listType.IsArray || !typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(listTypeInfo)) { return(null); } #else if (listType == model.MapType(typeof(string)) || listType.IsArray || !model.MapType(typeof(IEnumerable)).IsAssignableFrom(listType)) { return(null); } #endif BasicList candidates = new BasicList(); #if WINRT foreach (MethodInfo method in listType.GetRuntimeMethods()) #else foreach (MethodInfo method in listType.GetMethods()) #endif { if (method.IsStatic || method.Name != "Add") { continue; } ParameterInfo[] parameters = method.GetParameters(); Type paramType; if (parameters.Length == 1 && !candidates.Contains(paramType = parameters[0].ParameterType)) { candidates.Add(paramType); } } string name = listType.Name; bool isQueueStack = name != null && (name.IndexOf("Queue", System.StringComparison.Ordinal) >= 0 || name.IndexOf("Stack", System.StringComparison.Ordinal) >= 0); #if !NO_GENERICS if (!isQueueStack) { TestEnumerableListPatterns(model, candidates, listType); #if WINRT foreach (Type iType in listTypeInfo.ImplementedInterfaces) { TestEnumerableListPatterns(model, candidates, iType); } #else foreach (Type iType in listType.GetInterfaces()) { TestEnumerableListPatterns(model, candidates, iType); } #endif } #endif #if WINRT // more convenient GetProperty overload not supported on all platforms foreach (PropertyInfo indexer in listType.GetRuntimeProperties()) { if (indexer.Name != "Item" || candidates.Contains(indexer.PropertyType)) { continue; } ParameterInfo[] args = indexer.GetIndexParameters(); if (args.Length != 1 || args[0].ParameterType != typeof(int)) { continue; } MethodInfo getter = indexer.GetMethod; if (getter == null || getter.IsStatic) { continue; } candidates.Add(indexer.PropertyType); } #else // more convenient GetProperty overload not supported on all platforms foreach (PropertyInfo indexer in listType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (indexer.Name != "Item" || candidates.Contains(indexer.PropertyType)) { continue; } ParameterInfo[] args = indexer.GetIndexParameters(); if (args.Length != 1 || args[0].ParameterType != model.MapType(typeof(int))) { continue; } candidates.Add(indexer.PropertyType); } #endif switch (candidates.Count) { case 0: return(null); case 1: return((Type)candidates[0]); case 2: if (CheckDictionaryAccessors(model, (Type)candidates[0], (Type)candidates[1])) { return((Type)candidates[0]); } if (CheckDictionaryAccessors(model, (Type)candidates[1], (Type)candidates[0])) { return((Type)candidates[1]); } break; } return(null); }
private void CascadeDependents(BasicList list, MetaType metaType) { MetaType tmp; metaType.FinalizeSettingsValue(); if (metaType.IsList) { Type itemType = TypeModel.GetListItemType(this, metaType.Type); WireType defaultWireType; IProtoSerializer coreSerializer = this.ValueSerializerBuilder.TryGetSimpleCoreSerializer(BinaryDataFormat.Default, itemType, out defaultWireType); if (coreSerializer == null) { int index = FindOrAddAuto(itemType, false, false, false); if (index >= 0) { tmp = ((MetaType)_types[index]).GetSurrogateOrBaseOrSelf(false); if (!list.Contains(tmp)) { // could perhaps also implement as a queue, but this should work OK for sane models list.Add(tmp); CascadeDependents(list, tmp); } } } } else { if (metaType.GetFinalSettingsCopy().IsAutoTuple) { MemberInfo[] mapping; if (MetaType.ResolveTupleConstructor(metaType.Type, out mapping) != null) { for (int i = 0; i < mapping.Length; i++) { CscadeDependents_Member(list, (mapping[i] as PropertyInfo)?.PropertyType ?? (mapping[i] as FieldInfo)?.FieldType); } } } else { foreach (ValueMember member in metaType.Fields) { member.Serializer.GetHashCode(); var s = member.GetFinalSettingsCopy(0); Type type = s.Collection.ItemType ?? member.MemberType; var fieldMetaType = FindWithoutAdd(type); if (fieldMetaType != null) { type = fieldMetaType.GetSurrogateOrSelf().Type; } CscadeDependents_Member(list, type); } } if (metaType.HasSubtypes) { foreach (SubType subType in metaType.GetSubtypes()) { tmp = subType.DerivedType.GetSurrogateOrSelf(); // note: exclude base-types! if (!list.Contains(tmp)) { list.Add(tmp); CascadeDependents(list, tmp); } } } tmp = metaType.BaseType; tmp = tmp?.GetSurrogateOrSelf(); // note: already walking base-types; exclude base if (tmp != null && !list.Contains(tmp)) { list.Add(tmp); CascadeDependents(list, tmp); } } }
/// <summary> /// Suggest a .proto definition for the given type /// </summary> /// <param name="type">The type to generate a .proto definition for, or <c>null</c> to generate a .proto that represents the entire model</param> /// <returns>The .proto definition as a string</returns> public override string GetSchema(Type type) { BasicList requiredTypes = new BasicList(); MetaType primaryType = null; bool isInbuiltType = false; if (type == null) { // generate for the entire model for (int i = _serviceTypesCount; i < _types.Count; i++) { MetaType meta = (MetaType)_types[i]; MetaType tmp = meta.GetSurrogateOrBaseOrSelf(false); if (!requiredTypes.Contains(tmp)) { // ^^^ note that the type might have been added as a descendent requiredTypes.Add(tmp); CascadeDependents(requiredTypes, tmp); } } } else { Type tmp = Helpers.GetNullableUnderlyingType(type); if (tmp != null) { type = tmp; } WireType defaultWireType; isInbuiltType = (this.ValueSerializerBuilder.TryGetSimpleCoreSerializer(BinaryDataFormat.Default, type, out defaultWireType) != null); if (!isInbuiltType) { //Agenerate just relative to the supplied type int index = FindOrAddAuto(type, false, false, false); if (index < 0) { throw new ArgumentException("The type specified is not a contract-type", nameof(type)); } // get the required types primaryType = ((MetaType)_types[index]).GetSurrogateOrBaseOrSelf(false); requiredTypes.Add(primaryType); CascadeDependents(requiredTypes, primaryType); } } // use the provided type's namespace for the "package" StringBuilder headerBuilder = new StringBuilder(); string package = null; if (!isInbuiltType) { IEnumerable typesForNamespace = primaryType == null ? (IEnumerable)MetaTypes : requiredTypes; foreach (MetaType meta in typesForNamespace) { meta.FinalizeSettingsValue(); if (meta.IsList) { continue; } string tmp = meta.Type.Namespace; if (!Helpers.IsNullOrEmpty(tmp)) { if (tmp.StartsWith("System.", StringComparison.Ordinal)) { continue; } if (package == null) { // haven't seen any suggestions yet package = tmp; } else if (package == tmp) { // that's fine; a repeat of the one we already saw } else { // something else; have confliucting suggestions; abort package = null; break; } } } } if (!Helpers.IsNullOrEmpty(package)) { headerBuilder.Append("package ").Append(package).Append(';'); Helpers.AppendLine(headerBuilder); } bool requiresBclImport = false; StringBuilder bodyBuilder = new StringBuilder(); // sort them by schema-name MetaType[] metaTypesArr = new MetaType[requiredTypes.Count]; requiredTypes.CopyTo(metaTypesArr, 0); Array.Sort(metaTypesArr, MetaType.Comparer.Default); // write the messages if (isInbuiltType) { Helpers.AppendLine(bodyBuilder).Append("message ").Append(type.Name).Append(" {"); MetaType.NewLine(bodyBuilder, 1).Append("optional ").Append(GetSchemaTypeName(type, BinaryDataFormat.Default, false, false, ref requiresBclImport)) .Append(" value = 1;"); Helpers.AppendLine(bodyBuilder).Append('}'); } else { for (int i = 0; i < metaTypesArr.Length; i++) { MetaType tmp = metaTypesArr[i]; if (tmp.IsList && tmp != primaryType) { continue; } tmp.WriteSchema(bodyBuilder, 0, ref requiresBclImport); } } if (requiresBclImport) { headerBuilder.Append("import \"bcl.proto\"; // schema for protobuf-net's handling of core .NET types"); Helpers.AppendLine(headerBuilder); } return(Helpers.AppendLine(headerBuilder.Append(bodyBuilder)).ToString()); }
/// <summary> /// Adds a known sub-type to the inheritance model /// </summary> public MetaType AddSubType(int fieldNumber, Type derivedType) { if (derivedType == null) { throw new ArgumentNullException(nameof(derivedType)); } if (fieldNumber < 1) { throw new ArgumentOutOfRangeException(nameof(fieldNumber)); } if (Type.IsArray) { throw new ArgumentException("An array has inbuilt behaviour and cannot be subclassed"); } if (derivedType.IsArray) { throw new ArgumentException("An array has inbuilt behaviour and cannot be as used as a subclass"); } #if WINRT if (!CanHaveSubType(_typeInfo)) { #else if (!CanHaveSubType(Type)) { #endif throw new InvalidOperationException("Sub-types can only be added to non-sealed classes"); } if (!IsValidSubType(derivedType)) { throw new ArgumentException(derivedType.Name + " is not a valid sub-type of " + Type.Name, nameof(derivedType)); } if (_subTypesSimple != null && _subTypesSimple.Contains(derivedType)) { return(this); // already exists } if (!IsFieldFree(fieldNumber)) { throw new ArgumentException(string.Format("FieldNumber {0} was already taken in type {1}, can't add sub-type {2}", fieldNumber, Type.Name, derivedType.Name), nameof(fieldNumber)); } if (_subTypesSimple == null) { _subTypesSimple = new BasicList(); } _subTypesSimple.Add(derivedType); MetaType derivedMeta = _model[derivedType]; ThrowIfFrozen(); derivedMeta.ThrowIfFrozen(); SubType subType = new SubType(fieldNumber, derivedMeta); ThrowIfFrozen(); derivedMeta.SetBaseType(this); // includes ThrowIfFrozen if (_subTypes == null) { _subTypes = new BasicList(); } _subTypes.Add(subType); return(this); }