static void AddMetadata(
     Node node,
     ProjectObjectIdentifiers ids,
     ConcurrentDictionary <ObjectIdentifier, IEnumerable <ObjectIdentifier> > metadata,
     Func <IDataType, ImmutableList <ObjectIdentifier> > enqueue)
 {
     ids.TryGetIdentifier(node).Do(id =>
                                   metadata.AddOrUpdate(
                                       key: id,
                                       addValueFactory: _ =>
                                       node.MatchWith(
                                           (DocumentScope ds) => ds.MatchWith(
                                               (ClassNode cn) => enqueue(cn.BaseType),
                                               (TemplateNode tn) => enqueue(tn.ProducedType)),
                                           (ObjectNode on) => enqueue(on.DataType),
                                           (Uno.UX.Markup.UXIL.PropertyNode pn) => ImmutableList <ObjectIdentifier> .Empty,
                                           (DependencyNode dn) => ImmutableList <ObjectIdentifier> .Empty),
                                       updateValueFactory: (_, v) => v));
 }
        public static ProjectMetadata GenerateMetadata(
            this Project project,
            ProjectObjectIdentifiers ids)
        {
            var hierarchy           = new ConcurrentDictionary <ObjectIdentifier, IEnumerable <ObjectIdentifier> >();
            var precompiledElements = new List <PrecompiledElement>();

            var nodeWorklist = new ConcurrentQueue <Node>();
            var typeWorklist = new ConcurrentQueue <DataType>();

            foreach (var doc in project.RootClasses)
            {
                nodeWorklist.Enqueue(doc.Root);
            }

            Node node;

            while (nodeWorklist.TryDequeue(out node))
            {
                AddMetadata(node, ids, hierarchy,
                            dt =>
                {
                    var uxClass = dt as ClassNode;
                    if (uxClass != null)
                    {
                        return(ids
                               .TryGetIdentifier(uxClass)
                               .Select(id => new ObjectIdentifier(id.ToString()))
                               .ToList());
                    }

                    return(TryGetUnoDataType(dt)
                           .MatchWith(
                               none: () => ImmutableList <ObjectIdentifier> .Empty,
                               some: unoDataType =>
                    {
                        typeWorklist.Enqueue(unoDataType);

                        return List.Create(new ObjectIdentifier(unoDataType.FullName));
                    }));
                });

                foreach (var child in node.Children)
                {
                    nodeWorklist.Enqueue(child);
                }
            }

            DataType type;

            while (typeWorklist.TryDequeue(out type))
            {
                if (IsUxDeclarable(type))
                {
                    precompiledElements.Add(new PrecompiledElement(
                                                id: new ObjectIdentifier(type.FullName),
                                                source:
                                                "<" + type.Base.FullName + " ux:Class=\"" + type.FullName + "\">" +
                                                type.Properties
                                                .Where(p => IsUxDeclarable(p.ReturnType))
                                                .Select(p => "<" + p.ReturnType.FullName + " ux:Property=\"" + p.Name + "\" />")
                                                .Join("") +
                                                "</" + type.Base.FullName + ">"));
                }

                hierarchy.AddOrUpdate(
                    key: new ObjectIdentifier(type.FullName),
                    addValueFactory: _ =>
                {
                    var baseType = type.Base;
                    if (baseType == null)
                    {
                        return(ImmutableList <ObjectIdentifier> .Empty);
                    }

                    typeWorklist.Enqueue(baseType);
                    return(List.Create(new ObjectIdentifier(baseType.FullName)));
                },
                    updateValueFactory: (_, v) => v);
            }

            return(new ProjectMetadata(hierarchy.ToImmutableList(), precompiledElements.ToImmutableList()));
        }