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);
        }
Exemple #2
0
        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
            });
        }
Exemple #12
0
 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);
        }