private static void LoadDataSources(CanvasDocument app, DirectoryReader directory, ErrorContainer errors)
        {
            var tableDefs = new Dictionary <string, DataSourceDefinition>();

            app._dataSourceReferences = new Dictionary <string, LocalDatabaseReferenceJson>();

            foreach (var file in directory.EnumerateFiles(DataSourcePackageDir, "*.json"))
            {
                var tableDef = file.ToObject <DataSourceDefinition>();
                tableDefs.Add(tableDef.EntityName, tableDef);
                if (tableDef.DatasetName == null)
                {
                    continue;
                }

                if (!app._dataSourceReferences.TryGetValue(tableDef.DatasetName, out var localDatabaseReferenceJson))
                {
                    localDatabaseReferenceJson = new LocalDatabaseReferenceJson()
                    {
                        dataSources   = new Dictionary <string, LocalDatabaseReferenceDataSource>(),
                        ExtensionData = tableDef.ExtensionData,
                        instanceUrl   = tableDef.InstanceUrl
                    };
                    app._dataSourceReferences.Add(tableDef.DatasetName, localDatabaseReferenceJson);
                }
                if (localDatabaseReferenceJson.instanceUrl != tableDef.InstanceUrl)
                {
                    // Generate an error, dataset defs have diverged in a way that shouldn't be possible
                    // Each dataset has one instanceurl
                    errors.ValidationError($"For file {file._relativeName}, the dataset {tableDef.DatasetName} has multiple instanceurls");
                    throw new DocumentException();
                }

                localDatabaseReferenceJson.dataSources.Add(tableDef.EntityName, tableDef.LocalReferenceDSJson);
            }

            // key is filename, value is stringified xml
            var xmlDefs = new Dictionary <string, string>();

            foreach (var file in directory.EnumerateFiles(WadlPackageDir, "*.xml"))
            {
                xmlDefs.Add(Path.GetFileNameWithoutExtension(file._relativeName), file.GetContents());
            }

            // key is filename, value is stringified json
            var swaggerDefs = new Dictionary <string, string>();

            foreach (var file in directory.EnumerateFiles(SwaggerPackageDir, "*.json"))
            {
                swaggerDefs.Add(Path.GetFileNameWithoutExtension(file._relativeName), file.GetContents());
            }

            foreach (var file in directory.EnumerateFiles(DataSourcesDir, "*"))
            {
                var dataSources = file.ToObject <List <DataSourceEntry> >();
                foreach (var ds in dataSources)
                {
                    if (tableDefs.TryGetValue(ds.RelatedEntityName ?? ds.Name, out var definition))
                    {
                        switch (ds.Type)
                        {
                        case "NativeCDSDataSourceInfo":
                            ds.DatasetName     = definition.DatasetName;
                            ds.TableDefinition = JsonSerializer.Serialize(definition.TableDefinition, Utility._jsonOpts);
                            break;

                        case "ConnectedDataSourceInfo":
                            ds.DataEntityMetadataJson = definition.DataEntityMetadataJson;
                            ds.TableName = definition.TableName;
                            break;

                        case "OptionSetInfo":
                            ds.DatasetName = ds.DatasetName != string.Empty? definition.DatasetName : null;
                            break;

                        case "ServiceInfo":
                        case "ViewInfo":
                        default:
                            break;
                        }
                    }
                    else if (ds.Type == "ServiceInfo")
                    {
                        var foundXML  = xmlDefs.TryGetValue(Path.GetFileNameWithoutExtension(file._relativeName), out string xmlDef);
                        var foundJson = swaggerDefs.TryGetValue(Path.GetFileNameWithoutExtension(file._relativeName), out string swaggerDef);
                        if (!foundXML && !foundJson)
                        {
                            errors.ValidationError($"No matching wadl or swagger def for {file._relativeName}");
                            throw new DocumentException();
                        }
                        ds.WadlMetadata = new WadlDefinition()
                        {
                            WadlXml = xmlDef, SwaggerJson = swaggerDef
                        };
                    }

                    app.AddDataSourceForLoad(ds);
                }
            }
        }
        // Full fidelity read-write

        public static CanvasDocument LoadFromSource(string directory2, ErrorContainer errors)
        {
            if (File.Exists(directory2))
            {
                if (directory2.EndsWith(".msapp", StringComparison.OrdinalIgnoreCase))
                {
                    throw new ArgumentException($"Must point to a source directory, not an msapp file ({directory2}");
                }
            }

            if (!Directory.Exists(directory2))
            {
                throw new InvalidOperationException($"No directory {directory2}");
            }
            var dir = new DirectoryReader(directory2);
            var app = new CanvasDocument();

            // Do the manifest check (and version check) first.
            // MAnifest lives in top-level directory.
            foreach (var file in dir.EnumerateFiles("", "*.json"))
            {
                switch (file.Kind)
                {
                case FileKind.CanvasManifest:
                    var manifest = file.ToObject <CanvasManifestJson>();

                    if (manifest.FormatVersion != CurrentSourceVersion)
                    {
                        errors.FormatNotSupported($"This tool only supports {CurrentSourceVersion}, the manifest version is {manifest.FormatVersion}");
                        throw new DocumentException();
                    }

                    app._properties  = manifest.Properties;
                    app._header      = manifest.Header;
                    app._publishInfo = manifest.PublishInfo;
                    app._screenOrder = manifest.ScreenOrder;
                    break;

                case FileKind.Templates:
                    foreach (var kvp in file.ToObject <List <KeyValuePair <string, CombinedTemplateState> > >())
                    {
                        app._templateStore.AddTemplate(kvp.Key, kvp.Value);
                    }
                    break;

                case FileKind.ComponentReferences:
                    var refs = file.ToObject <ComponentDependencyInfo[]>();
                    app._libraryReferences = refs;
                    break;
                }
            }
            if (app._header == null)
            {
                // Manifest not found.
                errors.FormatNotSupported($"Can't find CanvasManifest.json file - is sources an old version?");
                throw new DocumentException();
            }

            // Load template files, recreate References/templates.json
            LoadTemplateFiles(errors, app, Path.Combine(directory2, PackagesDir), out var templateDefaults);

            foreach (var file in dir.EnumerateFiles(AssetsDir))
            {
                if (file._relativeName == "Resources.json")
                {
                    app._resourcesJson = file.ToObject <ResourcesJson>();
                    continue;
                }
                app.AddAssetFile(file.ToFileEntry());
            }

            // var root = Path.Combine(directory, OtherDir);
            foreach (var file in dir.EnumerateFiles(OtherDir))
            {
                // Special files like Header / Properties
                switch (file.Kind)
                {
                default:
                    // Track any unrecognized files so we can save back.
                    app.AddFile(file.ToFileEntry());
                    break;

                case FileKind.Entropy:
                    app._entropy = file.ToObject <Entropy>();
                    break;

                case FileKind.Checksum:
                    app._checksum = file.ToObject <ChecksumJson>();
                    break;

                case FileKind.Themes:
                    app._themes = file.ToObject <ThemesJson>();
                    break;

                case FileKind.Header:
                case FileKind.Properties:
                    throw new NotSupportedException($"Old format");

                case FileKind.ComponentSrc:
                case FileKind.ControlSrc:
                    // Shouldn't find any here -  were explicit in source
                    throw new InvalidOperationException($"Unexpected source file: " + file._relativeName);
                }
            } // each loose file in '\other'


            app.GetLogoFile();

            LoadDataSources(app, dir, errors);
            LoadSourceFiles(app, dir, templateDefaults, errors);

            foreach (var file in dir.EnumerateFiles(ConnectionDir))
            {
                // Special files like Header / Properties
                switch (file.Kind)
                {
                case FileKind.Connections:
                    app._connections = file.ToObject <IDictionary <string, ConnectionJson> >();
                    break;
                }
            }


            // Defaults.
            // - DynamicTypes.Json, Resources.Json , Templates.Json - could all be empty
            // - Themes.json- default to


            app.OnLoadComplete(errors);

            return(app);
        }
 private static IEnumerable <DirectoryReader.Entry> EnumerateComponentDirs(
     DirectoryReader directory, string pattern)
 {
     return(directory.EnumerateFiles(ComponentCodeDir, pattern).Concat(
                directory.EnumerateFiles(ComponentPackageDir, pattern)));
 }