/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of xAPI4Unity. */ #if UNITY_EDITOR using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; namespace xAPI4Unity.Editor.Parser.IO { /// /// Manages the read and write operations of a source type /// internal abstract class IOManager { /// /// Logging event /// public event Action OnLog; protected void Log(string message) => OnLog?.Invoke(message); /// /// Creates an IOManager /// protected IOManager() { Debug.Assert(HasSourceKey(GetType())); } private static bool HasSourceKey(Type type) { return type .GetProperties() .Any(p => p.Name == "SourceKey" && p.PropertyType == typeof(SourceType) && p.GetAccessors().All(ac => ac.IsPublic || ac.IsStatic)); } /// /// Gets the SourceKeys of all classes that inherits from IOManager /// /// List of the SourceKeys private static IEnumerable GetSubClassesSourceKeys() { var sourceTypes = typeof(IOManager).Assembly.GetTypes().Where(t => t.BaseType == typeof(IOManager)); return sourceTypes .Where(HasSourceKey) .Select(st => (SourceType)st.GetProperty("SourceKey").GetValue(null)).ToList(); } public static string[] GetSubClassesSourceKeyStrings() { var sourceKeys = GetSubClassesSourceKeys(); return sourceKeys.Select(s => s.GetSourceKeyString()).ToArray(); } /// /// Lists the contexts with all definitions /// /// Path with the contexts /// Files and folders to be excluded /// List of contexts public virtual List ListContextsFull(string rootPath, string[] exclude = null) { var contexts = new List(); var contextMetas = ListContexts(rootPath, exclude); foreach (var contextMeta in contextMetas) { var context = new Context(contextMeta); context.AddActivities(ListActivities(contextMeta)); context.AddExtensions(ListAllExtensions(contextMeta)); context.AddVerbs(ListVerbs(contextMeta)); contexts.Add(context); } return contexts; } public abstract List ListDeep(string rootPath, string[] includes = null); public abstract IEnumerable GetAllKeys(string rootPath); public abstract IEnumerable GetAllEntries(); /// /// Reads the contexts /// /// Directory that the definitions are in /// Files and folders to exclude /// Files and folders to be included /// List of contexts that have been read public virtual List ReadContexts(string rootPath, string[] exclude = null) { var contextMetas = ListContexts(rootPath, exclude); var contexts = contextMetas.Select(ReadContext).ToList(); return contexts; } /// /// Asynchronously reads the contexts /// /// Directory that the definitions are in /// Files and folders to exclude /// List of contexts that have been read public async Task> ReadContextsAsync(string rootPath, string[] exclude = null) { var contexts = new List(); var contextMetas = ListContexts(rootPath, exclude); var tasks = contextMetas.Select(ReadContextAsync).ToList(); for (var i = 0; i < contextMetas.Count; ++i) { contexts.Add(await tasks[i]); } return contexts; } /// /// Reads the Contents of the Files in all contexts /// /// Contexts to be read /// Filled Contexts public virtual List ReadContexts(List contexts) { foreach (var context in contexts) { ReadContext(context); } return contexts; } /// /// Asynchronously reads the Contents of the Files in all contexts /// /// Contexts to be read /// Filled Contexts public virtual async Task> ReadContextsAsync(List contexts) { var tasks = contexts.Select(ReadContextAsync).ToList(); await Task.WhenAll(tasks); return contexts; } /// /// Reads a context /// /// Context to read /// Context that has been read protected virtual Context ReadContext(FileMeta contextMeta) { var context = new Context(contextMeta); context.AddActivities(ReadActivities(contextMeta)); context.AddExtensions(ReadExtensions(contextMeta)); context.AddVerbs(ReadVerbs(contextMeta)); return context; } /// /// Asynchronously reads a context /// /// Context to read /// Context that has been read protected virtual async Task ReadContextAsync(FileMeta contextMeta) { var context = new Context(contextMeta); context.AddActivities(await ReadActivitiesAsync(contextMeta)); context.AddExtensions(await ReadExtensionsAsync(contextMeta)); context.AddVerbs(await ReadVerbsAsync(contextMeta)); return context; } /// /// Reads the Contents of the Files in the context /// /// Context to be read /// Filled context protected virtual Context ReadContext(Context context) { ReadDefinitions(context.Activities); foreach (var extList in context.Extensions.Values) { ReadDefinitions(extList); } ReadDefinitions(context.Verbs); return context; } /// /// Asynchronously reads the Contents of the Files in the context /// /// Context to be read /// Filled context protected virtual async Task ReadContextAsync(Context context) { await ReadDefinitionsAsync(context.Activities); foreach (var extList in context.Extensions.Values) { await ReadDefinitionsAsync(extList); } await ReadDefinitionsAsync(context.Verbs); return context; } /// /// Reads the activities of a context /// /// Context to read from /// List of activity files that have been read protected virtual IEnumerable ReadActivities(FileMeta context) { return ReadFiles(ListActivities(context)); } /// /// Asynchronously reads the activities of a context /// /// Context to read from /// List of activity files that have been read protected virtual async Task> ReadActivitiesAsync(FileMeta context) { return await ReadFilesAsync(ListActivities(context)); } /// /// Reads listed definitions /// /// Definitions to read /// List of definitions passed in protected virtual List ReadDefinitions(List definitions) { foreach (var definition in definitions) { if (definition.IsDirectory) { definition.DirContent = ReadFiles(definition.DirContent); } else { definition.FileContent = ReadFile(definition.Path); } } return definitions; } /// /// Asynchronously reads listed definitions /// /// Definitions to read /// List of definitions passed in protected virtual async Task> ReadDefinitionsAsync(List definitions) { foreach (var definition in definitions) { if (definition.IsDirectory) { definition.DirContent = await ReadFilesAsync(definition.DirContent); } else { definition.FileContent = await ReadFileAsync(definition.Path); } } return definitions; } /// /// Reads the extensions of a context /// /// Context to read from /// List of extension files that have been read protected virtual IDictionary> ReadExtensions(FileMeta context) { var allExtensions = new Dictionary>(); var extensionTypes = ListExtensionTypes(context); foreach (var extensionType in extensionTypes) { var extensions = ReadExtensions(context, extensionType); allExtensions.Add(extensionType.Name, extensions); } return allExtensions; } /// /// Asynchronously reads the extensions of a context /// /// Context to read from /// List of extension files that have been read protected virtual async Task>> ReadExtensionsAsync(FileMeta context) { var allExtensions = new Dictionary>(); var extensionTypes = ListExtensionTypes(context); foreach (var extensionType in extensionTypes) { var extensions = await ReadExtensionsAsync(context, extensionType); allExtensions.Add(extensionType.Name, extensions); } return allExtensions; } /// /// Reads the extensions of a context /// /// Context to read from /// Type of extension to read /// List of extension files that have been read protected virtual List ReadExtensions(FileMeta context, FileMeta type) { return ReadFiles(ListExtensions(type)); } /// /// Asynchronously reads the extensions of a context /// /// Context to read from /// Type of extension to read /// List of extension files that have been read protected virtual async Task> ReadExtensionsAsync(FileMeta context, FileMeta type) { return await ReadFilesAsync(ListExtensions(type)); } /// /// Reads the verbs of a context /// /// Context to read from /// List of verb files that have been read protected virtual List ReadVerbs(FileMeta context) { return ReadFiles(ListVerbs(context)); } /// /// Asynchronously reads the verbs of a context /// /// Context to read from /// List of verb files that have been read protected virtual async Task> ReadVerbsAsync(FileMeta context) { return await ReadFilesAsync(ListVerbs(context)); } /// /// Reads files /// /// List of files to read /// List of files that have been read protected abstract List ReadFiles(List files); /// /// Reads files asynchronously /// /// List of files to read /// Task that returns list of files that have been read protected abstract Task> ReadFilesAsync(List files); /// /// Reads file /// /// Path of the file to read /// Content of the file protected abstract string ReadFile(string path); /// /// Reads file asynchronously /// /// File to read /// Task that returns content of the file protected abstract Task ReadFileAsync(string path); /// /// Lists the activities in a context /// /// Context whose activities are listed /// List of activities in the context protected abstract List ListActivities(FileMeta context); /// /// Lists the contexts /// /// Path with the contexts /// Files and folders to be excluded /// List of contexts protected abstract List ListContexts(string rootPath, string[] exclude = null); public abstract List ReadContextsByDefs(string rootPath, IEnumerable includes); public abstract List GetFileMetasByDefs(string rootPath, IEnumerable includes); /// /// Lists the extension types in a context /// /// Context whose extension types are listed /// List of extension types in the context protected abstract List ListExtensionTypes(FileMeta context); /// /// Lists the extensions in a context /// /// Context whose extensions are listed /// List of extensions in the context protected abstract List ListExtensions(FileMeta contextExtType); /// /// Lists the verbs in a context /// /// Context whose verbs are listed /// List of verbs in the context protected abstract List ListVerbs(FileMeta context); /// /// Lists all extensions for a context /// /// Context whose extensions are listed /// Dictionary of extensions grouped by extension type protected virtual IDictionary> ListAllExtensions(FileMeta contextMeta) { var allExtensions = new Dictionary>(); foreach (var extType in ListExtensionTypes(contextMeta)) { var typeExtensions = ListExtensions(extType); allExtensions.Add(extType.Name, typeExtensions); } return allExtensions; } /// /// Combines two paths into one /// /// Paths to combine /// Combined Path protected abstract string CombinePaths(params string[] paths); /// /// Writes CodeFiles files of a given language /// /// Destination to write files to /// Core files to write /// Files to write /// Name of definitions directory /// Name of core directory public virtual void WriteCodeFiles(string dest, List coreFiles, string coreDirName, List files, string definitionsDirName) { WriteFiles(CombinePaths(dest, coreDirName), coreFiles); WriteFiles(CombinePaths(dest, definitionsDirName), files); } /// /// Asynchronously writes CodeFiles files of a given language /// /// Destination to write files to /// Core files to write /// Definitions files to write /// Name of definitions directory /// Name of core directory public virtual async Task WriteCodeFilesAsync(string dest, List coreFiles, string coreDirName, List files, string definitionsDirName) { var taskCore = WriteFilesAsync(CombinePaths(dest, coreDirName), coreFiles); var taskDefs = WriteFilesAsync(CombinePaths(dest, definitionsDirName), files); await Task.WhenAll(taskCore, taskDefs); } /// /// Writes files /// /// Destination to write to /// Files to write protected abstract void WriteFiles(string dest, List files); /// /// Asynchronously writes files /// /// Destination to write to /// Files to write protected abstract Task WriteFilesAsync(string dest, List files); } } #endif