public PropertyTypeNode.TypeTag Resolve( ContainerTypeTreePath context, string typeName) { if (string.IsNullOrWhiteSpace(typeName)) { return(PropertyTypeNode.TypeTag.Unknown); } // Handle trivial cases first if (typeName.ToLower() == "list") { return(PropertyTypeNode.TypeTag.List); } if (IsPrimitiveType(typeName)) { return(PropertyTypeNode.TypeTag.Primitive); } // Look up in the current known resolver's symbols var tag = ResolveWithBuiltinSymbols(context, typeName); if (tag != PropertyTypeNode.TypeTag.Unknown) { // Found it as an internal symbol return(tag); } // Look up the assemblies if (_referenceAssemblyNames == null) { return(PropertyTypeNode.TypeTag.Unknown); } var t = ResolveType(typeName); if (t == null) { return(PropertyTypeNode.TypeTag.Unknown); } if (t.IsClass) { return(PropertyTypeNode.TypeTag.Class); } if (t.IsEnum) { return(PropertyTypeNode.TypeTag.Enum); } if (t.IsValueType && t.IsLayoutSequential) { return(PropertyTypeNode.TypeTag.Struct); } return(PropertyTypeNode.TypeTag.Primitive); }
public static ContainerTypeTreePath CreateFromString(string fullpath) { if (string.IsNullOrEmpty(fullpath)) { return(new ContainerTypeTreePath()); } // The type path for the leaf type here: // namespace my.namespace { // class roottype { // class nested { // class types {} // } // } // } // is: // "my.namespace.roottype/nested/types" // for which the traversal path should be // "my.namespace" > "roottype" > "nested" > "types" var paths = fullpath.Split(NestedTypeNameSeparator[0]); var namespaceAndRootTypename = paths[0]; var nestedTypeNames = paths.Reverse().Take(paths.Length - 1).Reverse().ToList(); var rootTypeName = string.Empty; var nameSpace = string.Empty; { var topLevelPathParts = namespaceAndRootTypename.Split(TypeNameSeparator[0]); rootTypeName = topLevelPathParts.Last(); if (topLevelPathParts.Length > 1) { nameSpace = string.Join( TypeNameSeparator , topLevelPathParts.Take(topLevelPathParts.Length - 1)); } } var ttPath = new ContainerTypeTreePath() { Namespace = nameSpace, TypePath = new Stack <string>(new [] { rootTypeName }) }; foreach (var nestedTypeName in nestedTypeNames) { ttPath.TypePath.Push(nestedTypeName); } return(ttPath); }
public void VisitRoot(IContainerTypeTreeVisitor visitor) { var context = new ContainerTypeTreePath() { Namespace = this.TypePath.Namespace, TypePath = new Stack <string>(new[] { this.TypeName }) }; visitor.VisitContainer(context, this); Visit(context, visitor); }
public void Visit(ContainerTypeTreePath path, IContainerTypeTreeVisitor visitor) { path.TypePath.Push(TypeName); visitor.VisitNestedContainer(path, this); foreach (var t in NestedContainers) { t.Visit(path, visitor); } path.TypePath.Pop(); }
// @TODO extract as a visitor/tree iterator private static void CollectTypesFromTypeTree( IEnumerable types, Dictionary <string, PropertyTypeNode.TypeTag> builtinSymbols, ContainerTypeTreePath path = null) { foreach (var type in types.OfType <IDictionary <string, object> >()) { if (!type.ContainsKey(Keys.ContainerNameKey) || string.IsNullOrEmpty(type[Keys.ContainerNameKey] as string)) { continue; } var currentPath = path ?? new ContainerTypeTreePath(); currentPath.TypePath.Push((string)type[Keys.ContainerNameKey]); if (path != null) { currentPath.Namespace = type.ContainsKey(Keys.NamespaceKey) ? type[Keys.NamespaceKey] as string : string.Empty; } var tag = PropertyTypeNode.TypeTag.Class; if (type.ContainsKey(Keys.ContainerIsStructKey) && type[Keys.ContainerIsStructKey] is bool && (bool)type[Keys.ContainerIsStructKey]) { tag = PropertyTypeNode.TypeTag.Struct; } builtinSymbols[currentPath.FullPath] = tag; // recurse if (!type.ContainsKey(Keys.NestedTypesKey) || !(type[Keys.NestedTypesKey] is IEnumerable)) { continue; } CollectTypesFromTypeTree( (IEnumerable)type[Keys.NestedTypesKey], builtinSymbols, currentPath); } }
private PropertyTypeNode.TypeTag GetTypeTagFromPropertyName( string propertyTypeName, ContainerTypeTreePath context, PropertyTypeNode.TypeTag defaultTypeTag = PropertyTypeNode.TypeTag.Class) { var propertyTypeTag = defaultTypeTag; if (TypeResolver.IsPrimitiveType(propertyTypeName)) { propertyTypeTag = PropertyTypeNode.TypeTag.Primitive; } else if (_typeResolver != null) { propertyTypeTag = _typeResolver.Resolve(context, propertyTypeName); } return(propertyTypeTag); }
// @TODO when typeName is a qualified type name (contains namespace etc) private PropertyTypeNode.TypeTag ResolveWithBuiltinSymbols( ContainerTypeTreePath context, string typeName) { var tag = PropertyTypeNode.TypeTag.Unknown; if (context == null) { return(tag); } var typeTreePath = new ContainerTypeTreePath(context); while (typeTreePath.TypePath.Count != 0) { var currentFullyQualifiedTypeName = typeTreePath.WithNestedTypeName(typeName).FullPath; if (_builtinSymbols.ContainsKey(currentFullyQualifiedTypeName)) { return(_builtinSymbols[currentFullyQualifiedTypeName]); } typeTreePath.TypePath.Pop(); } // top level context if (!string.IsNullOrWhiteSpace(context.Namespace)) { var typePath = context.WithRootTypeName(typeName).FullPath; if (_builtinSymbols.ContainsKey(typePath)) { return(_builtinSymbols[typePath]); } } // Fallback to global namespace return(_builtinSymbols.ContainsKey(typeName) ? _builtinSymbols[typeName] : tag); }
private List <PropertyTypeNode> ParseProperties( IDictionary <string, object> d, ContainerTypeTreePath context) { var properties = new List <PropertyTypeNode>(); if (d != null && d.ContainsKey(JsonSchema.Keys.PropertiesListKey)) { // Empty props if not var propertiesAsJson = d[JsonSchema.Keys.PropertiesListKey] as IEnumerable; if (propertiesAsJson == null) { return(properties); } foreach (var p in propertiesAsJson) { var property = p as IDictionary <string, object>; if (property == null) { throw new Exception( $"Invalid property description found (expecting a JSON dictionary) for container {context.FullPath}."); } var propertyName = property[JsonSchema.Keys.PropertyNameKey] as string; if (string.IsNullOrEmpty(propertyName)) { throw new Exception( $"Invalid property name (empty) found in container {context.FullPath}."); } properties.Add(ParseProperty(propertyName, property, context)); } } return(properties); }
public void VisitContainer(ContainerTypeTreePath path, PropertyTypeNode container) { BuiltinTypes.Add(path.FullPath, container.Tag); }
private PropertyTypeNode ParseContainer( IDictionary <string, object> t, ContainerTypeTreePath context, bool ignoreNamespace = false) { var containerTypeName = SafeGetKeyValue(t, JsonSchema.Keys.ContainerNameKey); if (string.IsNullOrWhiteSpace(containerTypeName)) { throw new Exception( $"Invalid container type (empty) while parsing json (path: '{context.FullPath}')"); } if (!ignoreNamespace) { context.Namespace = SafeGetKeyValue(t, JsonSchema.Keys.NamespaceKey); } var generatedUserHooks = SafeGetKeyValue(t, JsonSchema.Keys.GeneratedUserHooksKey); var overrideDefaultBaseClass = SafeGetKeyValue(t, JsonSchema.Keys.OverrideDefaultBaseClassKey); bool isAbstractClass; if (!bool.TryParse( SafeGetKeyValue(t, JsonSchema.Keys.IsAbstractClassKey), out isAbstractClass)) { isAbstractClass = PropertyTypeNode.Defaults.IsAbstractClass; } bool noDefaultImplementation; if (!bool.TryParse( SafeGetKeyValue(t, JsonSchema.Keys.NoDefaultImplementationKey), out noDefaultImplementation)) { noDefaultImplementation = PropertyTypeNode.Defaults.NoDefaultImplementation; } var n = new PropertyTypeNode { TypePath = new ContainerTypeTreePath(context), TypeName = containerTypeName, Tag = GetTypeTagFromPropertyName(containerTypeName, context), UserHooks = UserHooks.From(generatedUserHooks), OverrideDefaultBaseClass = overrideDefaultBaseClass, IsAbstractClass = isAbstractClass, Properties = ParseProperties(t, context), NativeType = TryExtractPropertyNativeType(containerTypeName), NoDefaultImplementation = noDefaultImplementation, }; if (t.ContainsKey(JsonSchema.Keys.ConstructedFromKey)) { var constructorParams = t[JsonSchema.Keys.ConstructedFromKey] as IEnumerable; var paramTypes = new List <KeyValuePair <string, string> >(); if (constructorParams != null) { paramTypes.AddRange(from object p in constructorParams select p as IDictionary <string, object> into dp let paramType = dp.ContainsKey(JsonSchema.Keys.PropertyTypeKey) ? (dp[JsonSchema.Keys.PropertyTypeKey] as string) : "" let paramName = dp.ContainsKey(JsonSchema.Keys.PropertyNameKey) ? (dp[JsonSchema.Keys.PropertyNameKey] as string) : "" where !string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(paramType) select new KeyValuePair <string, string>(paramType, paramName)); } n.Constructor.ParameterTypes = paramTypes; } if (!t.ContainsKey(JsonSchema.Keys.NestedTypesKey) || !(t[JsonSchema.Keys.NestedTypesKey] is IEnumerable)) { return(n); } var nestedContainers = (IEnumerable)t[JsonSchema.Keys.NestedTypesKey]; context.TypePath.Push(containerTypeName); foreach (var nestedContainerEnumerable in nestedContainers) { var nestedContainer = nestedContainerEnumerable as IDictionary <string, object>; if (nestedContainer == null) { continue; } n.NestedContainers.Add( ParseContainer( nestedContainer, context, true ) ); } context.TypePath.Pop(); return(n); }
private PropertyTypeNode ParseProperty( string propertyName, IDictionary <string, object> rawProperty, ContainerTypeTreePath context) { if (!rawProperty.ContainsKey(JsonSchema.Keys.PropertyTypeKey)) { return(null); } var propertyTypeName = SafeGetKeyValue(rawProperty, JsonSchema.Keys.PropertyTypeKey); var propertyTypeTag = GetTypeTagFromPropertyName(propertyTypeName, context); bool isPublicProperty; if (!bool.TryParse( SafeGetKeyValue(rawProperty, JsonSchema.Keys.PropertyIsPublicKey), out isPublicProperty)) { isPublicProperty = PropertyTypeNode.Defaults.IsPublicProperty; } var defaultValue = SafeGetKeyValue(rawProperty, JsonSchema.Keys.PropertyDefaultValueKey); var propertyItemTypeName = SafeGetKeyValue(rawProperty, JsonSchema.Keys.PropertyItemTypeKey); var propertyBackingAccessor = SafeGetKeyValue(rawProperty, JsonSchema.Keys.PropertyDelegateMemberToKey); bool isReadonlyProperty; if (!bool.TryParse( SafeGetKeyValue(rawProperty, JsonSchema.Keys.IsReadonlyPropertyKey), out isReadonlyProperty)) { isReadonlyProperty = PropertyTypeNode.Defaults.IsReadonly; } if (propertyTypeTag == PropertyTypeNode.TypeTag.List && string.IsNullOrEmpty(propertyItemTypeName)) { throw new Exception($"Property {propertyName} has 'list' type but not item type specifier"); } PropertyTypeNode subPropertyItem = null; if (!string.IsNullOrEmpty(propertyItemTypeName)) { subPropertyItem = new PropertyTypeNode { TypeName = propertyItemTypeName, Tag = GetTypeTagFromPropertyName(propertyItemTypeName, context, PropertyTypeNode.TypeTag.Primitive), NativeType = TryExtractPropertyNativeType(propertyItemTypeName) }; } bool isCustomProperty; if (!bool.TryParse( SafeGetKeyValue(rawProperty, JsonSchema.Keys.IsCustomPropertyKey), out isCustomProperty)) { isCustomProperty = PropertyTypeNode.Defaults.IsCustomProperty; } bool dontInitializeBackingField; if (!bool.TryParse( SafeGetKeyValue(rawProperty, JsonSchema.Keys.DontInitializeBackingFieldKey), out dontInitializeBackingField)) { dontInitializeBackingField = PropertyTypeNode.Defaults.DontInitializeBackingField; } return(new PropertyTypeNode { PropertyName = propertyName, TypeName = propertyTypeName, Tag = propertyTypeTag, DefaultValue = defaultValue, PropertyBackingAccessor = propertyBackingAccessor, Of = subPropertyItem, IsReadonly = isReadonlyProperty, IsPublicProperty = isPublicProperty, IsCustomProperty = isCustomProperty, NativeType = TryExtractPropertyNativeType(propertyTypeName), DontInitializeBackingField = dontInitializeBackingField }); }
public ContainerTypeTreePath(ContainerTypeTreePath other) { Namespace = other.Namespace; TypePath = new Stack <string>(other.TypePath.Reverse()); }
private bool GetOrCreate(string typeName, out PropertyTypeNode node) { if (_parsedContainerTypeNodesCache.ContainsKey(typeName)) { // Already created node = _parsedContainerTypeNodesCache[typeName]; return(false); } if (!_typesBeingSerialized.ContainsKey(typeName)) { // Ooops node = null; return(false); } var type = _typesBeingSerialized[typeName]; var n = _parsedContainerTypeNodesCache[typeName] = new PropertyTypeNode() { IsAbstractClass = type.IsAbstract, Tag = TypeTagFromTypeDefinition(type), UserHooks = type.Methods.FirstOrDefault(m => m.Name == UserHookFlags.OnPropertyBagConstructed.ToString()) != null ? UserHookFlags.OnPropertyBagConstructed : UserHookFlags.None, OverrideDefaultBaseClass = HasDirectInterfacesOf(type, typeof(IPropertyContainer).FullName) ? string.Empty : type.BaseType.Resolve().Name, TypeName = type.Name, Properties = SerializePropertyFieldsForType(type), NativeType = TypeFromTypeDefinition(type), TypePath = new ContainerTypeTreePath { Namespace = type.Namespace } }; // @TODO beware of circular deps if (type.IsNested) { var path = ContainerTypeTreePath.CreateFromString(type.FullName); path.TypePath.Pop(); n.TypePath = path; // if we are a nested class part of a non ipropertycontainer derived struct/class // we stop the traversal here if (!_typesBeingSerialized.ContainsKey(type.DeclaringType.FullName) && (type.DeclaringType.IsClass || type.DeclaringType.IsValueType)) { node = n; return(true); } else { // get the parent PropertyTypeNode parent = null; GetOrCreate(type.DeclaringType.FullName, out parent); if (!_typesBeingSerialized.ContainsKey(parent.FullTypeName)) { throw new Exception($"Invalid type definition tree when trying to recurse to {type.FullName}'s type parent"); } var parentDefinition = _typesBeingSerialized[parent.FullTypeName]; if (parent.NestedContainers.FirstOrDefault(c => c.FullTypeName == type.FullName) != null) { // Should not happen unless there is an issue with the cache throw new Exception( $"Cannot add duplicate nested child node {type.FullName} to parent {parent.FullTypeName}"); } parent.NestedContainers.Add(n); node = null; return(false); } } node = n; return(true); }