private static void LoadSourceFiles(CanvasDocument app, DirectoryReader directory, Dictionary <string, ControlTemplate> templateDefaults, ErrorContainer errors)
        {
            foreach (var file in directory.EnumerateFiles(EditorStateDir, "*.json"))
            {
                if (!file._relativeName.EndsWith(".editorstate.json"))
                {
                    errors.FormatNotSupported($"Unexpected file present in {EditorStateDir}");
                    throw new DocumentException();
                }

                // Json peer to a .pa file.
                var controlExtraData = file.ToObject <Dictionary <string, ControlState> >();
                var topParentName    = file._relativeName.Replace(".editorstate.json", "");
                foreach (var control in controlExtraData)
                {
                    control.Value.TopParentName = topParentName;
                    if (!app._editorStateStore.TryAddControl(control.Value))
                    {
                        // Can't have duplicate control names.
                        // This might happen due to a bad merge.
                        errors.EditorStateError(file.SourceSpan, $"Control '{control.Value.Name}' is already defined.");
                    }
                }
            }

            // For now, the Themes file lives in CodeDir as a json file
            // We'd like to make this .pa.yaml as well eventually
            foreach (var file in directory.EnumerateFiles(CodeDir, "*.json", searchSubdirectories: false))
            {
                if (Path.GetFileName(file._relativeName) == "Themes.json")
                {
                    app._themes = file.ToObject <ThemesJson>();
                }
            }


            foreach (var file in directory.EnumerateFiles(CodeDir, "*.pa.yaml", searchSubdirectories: false))
            {
                AddControl(app, file._relativeName, false, file.GetContents(), errors);
            }

            foreach (var file in EnumerateComponentDirs(directory, "*.pa.yaml"))
            {
                AddControl(app, file._relativeName, true, file.GetContents(), errors);
            }

            foreach (var file in directory.EnumerateFiles(TestDir, "*.pa.yaml"))
            {
                AddControl(app, file._relativeName, false, file.GetContents(), errors);
            }

            foreach (var file in EnumerateComponentDirs(directory, "*.json"))
            {
                var componentTemplate = file.ToObject <CombinedTemplateState>();
                app._templateStore.AddTemplate(componentTemplate.ComponentManifest.Name, componentTemplate);
            }
        }
        // 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 <Dictionary <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());
            }

            foreach (var file in dir.EnumerateFiles(EntropyDir))
            {
                switch (file.Kind)
                {
                case FileKind.Entropy:
                    app._entropy = file.ToObject <Entropy>();
                    break;

                case FileKind.AppCheckerResult:
                    app._appCheckerResultJson = file.ToObject <AppCheckerResultJson>();
                    break;

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

                default:
                    errors.GenericWarning($"Unexpected file in Entropy, discarding");
                    break;
                }
            }


            foreach (var file in dir.EnumerateFiles(OtherDir))
            {
                // Special files like Header / Properties
                switch (file.Kind)
                {
                case FileKind.Unknown:
                    // Track any unrecognized files so we can save back.
                    app.AddFile(file.ToFileEntry());
                    break;

                default:
                    // Shouldn't find anything else not unknown in here, but just ignore them for now
                    errors.GenericWarning($"Unexpected file in Other, discarding");
                    break;
                }
            } // 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 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 "ViewInfo":
                            if (definition != null)
                            {
                                RestoreViewName(ds, definition.DatasetName);
                            }
                            break;

                        case "ServiceInfo":
                        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)
                        {
                            ds.WadlMetadata = new WadlDefinition()
                            {
                                WadlXml = xmlDef, SwaggerJson = swaggerDef
                            };
                        }
                    }

                    app.AddDataSourceForLoad(ds);
                }
            }
        }
 private static IEnumerable <DirectoryReader.Entry> EnumerateComponentDirs(
     DirectoryReader directory, string pattern)
 {
     return(directory.EnumerateFiles(ComponentCodeDir, pattern).Concat(
                directory.EnumerateFiles(ComponentPackageDir, pattern)));
 }
        // Full fidelity read-write

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

            Utilities.VerifyDirectoryExists(errors, directory2);

            if (errors.HasErrors)
            {
                return(null);
            }

            var    dir = new DirectoryReader(directory2);
            var    app = new CanvasDocument();
            string appInsightsInstumentationKey = null;

            // 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 <Dictionary <string, CombinedTemplateState> >())
                    {
                        app._templateStore.AddTemplate(kvp.Key, kvp.Value);
                    }
                    break;

                case FileKind.ComponentReferences:
                    var refs = file.ToObject <ComponentDependencyInfo[]>();
                    app._libraryReferences = refs;
                    break;

                case FileKind.AppInsightsKey:
                    var appInsights = file.ToObject <AppInsightsKeyJson>();
                    appInsightsInstumentationKey = appInsights.InstrumentationKey;
                    break;
                }
            }
            if (appInsightsInstumentationKey != null)
            {
                app._properties.InstrumentationKey = appInsightsInstumentationKey;
            }
            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(EntropyDir))
            {
                switch (file.Kind)
                {
                case FileKind.Entropy:
                    app._entropy = file.ToObject <Entropy>();
                    break;

                case FileKind.AppCheckerResult:
                    app._appCheckerResultJson = file.ToObject <AppCheckerResultJson>();
                    break;

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

                default:
                    errors.GenericWarning($"Unexpected file in Entropy, discarding");
                    break;
                }
            }

            // The resource entries for sample data is sharded into individual json files.
            // Add each of these entries back into Resrouces.json
            var resources = new List <ResourceJson>();

            app._resourcesJson = new ResourcesJson()
            {
                Resources = new ResourceJson[0]
            };
            foreach (var file in dir.EnumerateFiles(AssetsDir, "*", false))
            {
                var fileEntry = file.ToFileEntry();
                if (fileEntry.Name.GetExtension() == ".json")
                {
                    // If its a json file then this must be one of the sharded files from Resources.json
                    resources.Add(file.ToObject <ResourceJson>());
                }
            }

            // Add the resources from sharded files to _resourcesJson.Resources
            if (resources.Count > 0)
            {
                app._resourcesJson.Resources = resources.ToArray();
            }

            // We have processed all the json files in Assets directory, now interate through all tge files to add the asset files.
            foreach (var file in dir.EnumerateFiles(AssetsDir))
            {
                // Skip adding the json files which were created to contain the information for duplicate asset files.
                // The name of the such json files is of the format - <assetFileName>.<assetFileExtension>.json (eg. close_1.jpg.json)
                var fileName = file._relativeName;
                var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);

                // Check if the original extension was .json and the remaining file name has still got an extension,
                // Then this is an additional file that was created to contain information for duplicate assets.
                if (Path.HasExtension(fileNameWithoutExtension) && Path.GetExtension(fileName) == ".json")
                {
                    var localAssetInfoJson = file.ToObject <LocalAssetInfoJson>();
                    app._localAssetInfoJson.Add(localAssetInfoJson.NewFileName, localAssetInfoJson);
                }
                // Add non json files to _assetFiles
                else if (Path.GetExtension(fileName) != ".json")
                {
                    app.AddAssetFile(file.ToFileEntry());
                }
            }

            app.GetLogoFile();

            // Add the entries for local assets back to resrouces.json
            TranformResourceJson.AddLocalAssetEntriesToResourceJson(app);

            foreach (var file in dir.EnumerateFiles(OtherDir))
            {
                // Special files like Header / Properties
                switch (file.Kind)
                {
                case FileKind.Unknown:
                    // Track any unrecognized files so we can save back.
                    app.AddFile(file.ToFileEntry());
                    break;

                default:
                    // Shouldn't find anything else not unknown in here, but just ignore them for now
                    errors.GenericWarning($"Unexpected file in Other, discarding");
                    break;
                }
            } // each loose file in '\other'

            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 void LoadSourceFiles(CanvasDocument app, DirectoryReader directory, Dictionary <string, ControlTemplate> templateDefaults, ErrorContainer errors)
        {
            foreach (var file in directory.EnumerateFiles(EditorStateDir, "*.json"))
            {
                if (!file._relativeName.EndsWith(".editorstate.json"))
                {
                    errors.FormatNotSupported($"Unexpected file present in {EditorStateDir}");
                    throw new DocumentException();
                }

                // Json peer to a .pa file.
                ControlTreeState editorState = file.ToObject <ControlTreeState>();
                if (editorState.ControlStates == null)
                {
                    ApplyV24BackCompat(editorState, file);
                }

                foreach (var control in editorState.ControlStates)
                {
                    control.Value.TopParentName = editorState.TopParentName;
                    if (!app._editorStateStore.TryAddControl(control.Value))
                    {
                        // Can't have duplicate control names.
                        // This might happen due to a bad merge.
                        errors.EditorStateError(file.SourceSpan, $"Control '{control.Value.Name}' is already defined.");
                    }
                }
            }

            // For now, the Themes file lives in CodeDir as a json file
            // We'd like to make this .fx.yaml as well eventually
            foreach (var file in directory.EnumerateFiles(CodeDir, "*.json", searchSubdirectories: false))
            {
                if (Path.GetFileName(file._relativeName) == "Themes.json")
                {
                    app._themes = file.ToObject <ThemesJson>();
                }
            }

            foreach (var file in EnumerateComponentDirs(directory, "*.fx.yaml"))
            {
                AddControl(app, file._relativeName, true, file.GetContents(), errors);
            }

            foreach (var file in EnumerateComponentDirs(directory, "*.json"))
            {
                var componentTemplate = file.ToObject <CombinedTemplateState>();
                app._templateStore.AddTemplate(componentTemplate.ComponentManifest.Name, componentTemplate);
            }

            foreach (var file in directory.EnumerateFiles(CodeDir, "*.fx.yaml", searchSubdirectories: false))
            {
                AddControl(app, file._relativeName, false, file.GetContents(), errors);
            }

            // When loading TestSuites sharded files, add them within the top parent AppTest control (i.e. Test_7F478737223C4B69)
            // Make sure to load the the Test_7F478737223C4B69.fx.yaml file first to add the top parent control.
            var shardedTestSuites = new List <DirectoryReader.Entry>();

            foreach (var file in directory.EnumerateFiles(TestDir, "*.fx.yaml"))
            {
                if (file.Kind == FileKind.AppTestParentControl)
                {
                    AddControl(app, file._relativeName, false, file.GetContents(), errors);
                }
                else
                {
                    shardedTestSuites.Add(file);
                }
            }
            shardedTestSuites.ForEach(x => AddControl(app, x._relativeName, false, x.GetContents(), errors));
        }