private static bool Compile( IEnumerable <I18nTextSourceFile> srcFiles, I18nTextCompilerOptions options, Action <I18nTextCompilerOptions, IEnumerable <I18nTextCompileItem> > beforeCompile, Action <I18nTextCompilerOptions, I18nTextCompileItem, IEnumerable <string> > saveCode, CancellationToken cancellationToken) { try { var i18textSrc = I18nTextSourceFile.Parse(srcFiles, options, cancellationToken); OutputI18nTextJsonFiles(options, i18textSrc, cancellationToken); OutputTypesFiles(options, i18textSrc, beforeCompile, saveCode, cancellationToken); return(true); } catch (AggregateException e) when(e.InnerException is I18nTextCompileException compileException) { options.LogError(compileException); return(false); } catch (I18nTextCompileException compileException) { options.LogError(compileException); return(false); } }
public static bool Compile( IEnumerable <I18nTextSourceFile> srcFiles, I18nTextCompilerOptions options, Action <I18nTextCompilerOptions, I18nTextCompileItem, IEnumerable <string> > saveCode, CancellationToken cancellationToken) { return(Compile(srcFiles, options, beforeCompile: null, saveCode, cancellationToken)); }
public override bool Execute() { try { if ((this.Include?.Length ?? 0) == 0) { this.Log.LogMessage("No Source files were specified."); return(true); } var baseDir = this.BaseDir ?? Path.GetDirectoryName(this.BuildEngine.ProjectFileOfTaskNode); var options = new I18nTextCompilerOptions(baseDir); options.I18nTextSourceDirectory = Path.Combine(baseDir, this.I18nTextSourceDirectory) ?? options.I18nTextSourceDirectory; options.TypesDirectory = this.TypesDirectory ?? options.TypesDirectory; options.OutDirectory = this.OutDirectory ?? options.OutDirectory; options.NameSpace = string.IsNullOrEmpty(this.NameSpace) ? options.NameSpace : this.NameSpace; options.DisableSubNameSpace = this.DisableSubNameSpace; options.FallBackLanguage = this.FallBackLanguage ?? options.FallBackLanguage; options.LogMessage = msg => this.Log.LogMessage(msg); options.LogError = ex => this.Log.LogError(ex is I18nTextCompileException compilerEx ? $"IN{compilerEx.Code:D3}: {ex.Message}" : ex.Message); this.Log.LogMessage($"I18nTextSourceDirectory: [{options.I18nTextSourceDirectory}]"); this.Log.LogMessage($"TypesDirectory: [{options.TypesDirectory}]"); this.Log.LogMessage($"OutDirectory : [{options.OutDirectory}]"); this.Log.LogMessage($"NameSpace : [{options.NameSpace}]"); this.Log.LogMessage($"FallBackLanguage: [{options.FallBackLanguage}]"); var srcFiles = this.Include .Select(taskItem => (Path: Path.Combine(baseDir, taskItem.ItemSpec), Encoding: GetEncoding(taskItem))) .Select(item => new I18nTextSourceFile(item.Path, item.Encoding)) .ToArray(); foreach (var src in srcFiles) { this.Log.LogMessage($"- {src.Path}, {src.Encoding.BodyName}"); } var compiler = new I18nTextCompiler(); var successOrNot = compiler.Compile(srcFiles, options); return(successOrNot); } catch (Exception e) { this.Log.LogErrorFromException(e, showStackTrace: true, showDetail: true, file: null); return(false); } }
private static void OutputI18nTextJsonFiles(I18nTextCompilerOptions options, I18nTextSource i18textSrc, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (!i18textSrc.Types.Any()) { return; } if (!Directory.Exists(options.OutDirectory)) { Directory.CreateDirectory(options.OutDirectory); } var types = i18textSrc.Types .SelectMany(type => type.Value.Langs.Select(lang => (lang, jsonPath: Path.Combine(options.OutDirectory, options.NameSpace + "." + type.Key + "." + lang.Key + ".json")))) .ToArray(); // Sweep old generated/should be purge text json files. var existsTextJsonFiles = Directory.GetFiles(options.OutDirectory, "*.json"); var shouldBeSweepedFiles = existsTextJsonFiles.Except(types.Select(t => t.jsonPath)); foreach (var shouldBeSweepedFile in shouldBeSweepedFiles) { cancellationToken.ThrowIfCancellationRequested(); File.Delete(shouldBeSweepedFile); } Parallel.ForEach(types, new ParallelOptions { CancellationToken = cancellationToken }, ((KeyValuePair <string, I18nTextTable> lang, string jsonPath)arg) => { var textTable = new SortedDictionary <string, string>(arg.lang.Value); var jsonText = JsonConvert.SerializeObject(textTable, Formatting.Indented); cancellationToken.ThrowIfCancellationRequested(); var skipOutput = false; if (File.Exists(arg.jsonPath)) { var prevJsonText = File.ReadAllText(arg.jsonPath); cancellationToken.ThrowIfCancellationRequested(); skipOutput = prevJsonText == jsonText; } if (!skipOutput) { File.WriteAllText(arg.jsonPath, jsonText); } });
private static void SaveTypeCodeToTypeFiles(I18nTextCompilerOptions options, I18nTextCompileItem compileItem, IEnumerable <string> typeCode) { var skipOutput = false; if (File.Exists(compileItem.TypeFilePath)) { var prevTypeCode = File.ReadAllLines(compileItem.TypeFilePath); skipOutput = prevTypeCode.SequenceEqual(typeCode); } if (!skipOutput) { if (!Directory.Exists(options.TypesDirectory)) { Directory.CreateDirectory(options.TypesDirectory); } File.WriteAllLines(compileItem.TypeFilePath, typeCode); } }
public bool Compile(IEnumerable <I18nTextSourceFile> srcFiles, I18nTextCompilerOptions options) { try { var i18textSrc = ParseSourceFiles(srcFiles, options); OutputTypesFiles(options, i18textSrc); OutputI18nTextJsonFiles(options, i18textSrc); return(true); } catch (AggregateException e) when(e.InnerException is I18nTextCompileException compileException) { options.LogError(compileException.Message); return(false); } catch (I18nTextCompileException compileException) { options.LogError(compileException.Message); return(false); } }
/// <summary> /// Sweep old generated/should be purge type files. /// </summary> internal static void SweepTypeFilesShouldBePurged(I18nTextCompilerOptions options, IEnumerable <I18nTextCompileItem> compilerItems, CancellationToken cancellationToken) { if (Directory.Exists(options.TypesDirectory)) { var existsTypeFiles = Directory.GetFiles(options.TypesDirectory, "*.cs"); var shouldBeSweepedFiles = existsTypeFiles.Except(compilerItems.Select(t => t.TypeFilePath)); foreach (var shouldBeSweepedFile in shouldBeSweepedFiles) { cancellationToken.ThrowIfCancellationRequested(); if (File.ReadLines(shouldBeSweepedFile).Any(line => line == GeneratedMarker)) { File.Delete(shouldBeSweepedFile); } } cancellationToken.ThrowIfCancellationRequested(); if (!Directory.GetFileSystemEntries(options.TypesDirectory).Any()) { Directory.Delete(options.TypesDirectory, recursive: false); } } }
private static I18nTextSource ParseSourceFiles(IEnumerable <I18nTextSourceFile> srcFiles, I18nTextCompilerOptions options) { var i18textSrc = new I18nTextSource(); if (!srcFiles.Any()) { return(i18textSrc); } var i18nSrcDir = options.I18nTextSourceDirectory; if (!i18nSrcDir.EndsWith(Path.DirectorySeparatorChar.ToString())) { i18nSrcDir += Path.DirectorySeparatorChar; } ConvertPath convertPath; if (options.DisableSubNameSpace) { convertPath = delegate(string srcPath) { return(Path.GetFileName(srcPath)); }; } else { convertPath = delegate(string srcPath) { return(srcPath.StartsWith(i18nSrcDir) ? srcPath.Substring(i18nSrcDir.Length) : Path.GetFileName(srcPath)); }; } Parallel.ForEach(srcFiles, srcFile => { var srcName = convertPath(srcFile.Path); var fnameParts = srcName.Split('.', Path.DirectorySeparatorChar); var typeName = string.Join(".", fnameParts.Take(fnameParts.Length - 2)); var langCode = fnameParts[fnameParts.Length - 2]; var srcText = File.ReadAllText(srcFile.Path, srcFile.Encoding); var textTable = DeserializeSrcText(srcText, Path.GetExtension(srcFile.Path).ToLower()); var type = i18textSrc.Types.GetOrAdd(typeName, new I18nTextType()); type.Langs[langCode] = textTable; }); Parallel.ForEach(i18textSrc.Types.Values, type => { type.TextKeys = type.Langs .SelectMany(lang => lang.Value) .Select(tt => tt.Key) .OrderBy(key => key) .Distinct() .ToList(); Parallel.ForEach(type.Langs, lang => { var textTable = lang.Value; foreach (var textKey in type.TextKeys.Where(k => !textTable.ContainsKey(k))) { var text = type.Langs.Keys .OrderBy(langCode => langCode.StartsWith("en") ? "0" : langCode) .Select(langCode => type.Langs[langCode].TryGetValue(textKey, out var t) ? t : null) .FirstOrDefault(t => t != null); textTable[textKey] = text ?? textKey; } }); });
private static I18nTextSource ParseSourceFiles(IEnumerable <I18nTextSourceFile> srcFiles, I18nTextCompilerOptions options) { var i18textSrc = new I18nTextSource(); if (!srcFiles.Any()) { return(i18textSrc); } Parallel.ForEach(srcFiles, srcFile => { var fnameParts = Path.GetFileNameWithoutExtension(srcFile.Path).Split('.'); var typeName = string.Join(".", fnameParts.Take(fnameParts.Length - 1)); var langCode = fnameParts.Last(); var srcText = File.ReadAllText(srcFile.Path, srcFile.Encoding); var textTable = DeserializeSrcText(srcText, Path.GetExtension(srcFile.Path).ToLower()); var type = i18textSrc.Types.GetOrAdd(typeName, new I18nTextType()); type.Langs[langCode] = textTable; }); Parallel.ForEach(i18textSrc.Types.Values, type => { type.TextKeys = type.Langs .SelectMany(lang => lang.Value) .Select(tt => tt.Key) .OrderBy(key => key) .Distinct() .ToList(); Parallel.ForEach(type.Langs, lang => { var textTable = lang.Value; foreach (var textKey in type.TextKeys.Where(k => !textTable.ContainsKey(k))) { var text = type.Langs.Keys .OrderBy(langCode => langCode.StartsWith("en") ? "0" : langCode) .Select(langCode => type.Langs[langCode].TryGetValue(textKey, out var t) ? t : null) .FirstOrDefault(t => t != null); textTable[textKey] = text ?? textKey; } }); });
private static void OutputTypesFiles( I18nTextCompilerOptions options, I18nTextSource i18textSrc, Action <I18nTextCompilerOptions, IEnumerable <I18nTextCompileItem> > beforeCompile, Action <I18nTextCompilerOptions, I18nTextCompileItem, IEnumerable <string> > saveCode, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (!i18textSrc.Types.Any()) { return; } var i18nTextCompilerItems = i18textSrc.Types.Select(type => { var typeFullName = options.NameSpace + "." + type.Key; var typeNameParts = typeFullName.Split('.'); var typeNamespace = string.Join(".", typeNameParts.Take(typeNameParts.Length - 1)); var typeName = typeNameParts.Last(); var typeFilePath = Path.Combine(options.TypesDirectory, typeFullName + ".cs"); return(new I18nTextCompileItem(type, typeNamespace, typeName, typeFilePath)); }).ToArray(); beforeCompile?.Invoke(options, i18nTextCompilerItems); cancellationToken.ThrowIfCancellationRequested(); Parallel.ForEach(i18nTextCompilerItems, new ParallelOptions { CancellationToken = cancellationToken }, comilerItem => { var langs = comilerItem.Type.Value.Langs; var langParts = options.FallBackLanguage.Split('-'); var fallbackLangs = langParts.Length > 1 ? new[] { options.FallBackLanguage, langParts[0] } : new[] { options.FallBackLanguage }; var fallbackLang = fallbackLangs.FirstOrDefault(lang => langs.ContainsKey(lang)); if (fallbackLang == null) { throw new I18nTextCompileException(DiagnosticCode.FallbackLangNotFound, $"Could not find an I18n source text file of fallback language '{options.FallBackLanguage}', for '{options.NameSpace}.{comilerItem.Type.Key}'."); } var textTable = langs[fallbackLang]; var hash = GenerateHash(comilerItem.Type.Value); cancellationToken.ThrowIfCancellationRequested(); var typeCode = new List <string>(); typeCode.Add(GeneratedMarker); typeCode.Add($"namespace {comilerItem.TypeNamespace}"); typeCode.Add("{"); typeCode.Add($" public partial class {comilerItem.TypeName} : global::Toolbelt.Blazor.I18nText.Interfaces.I18nTextFallbackLanguage, global::Toolbelt.Blazor.I18nText.Interfaces.I18nTextLateBinding, global::Toolbelt.Blazor.I18nText.Interfaces.I18nTextTableHash"); typeCode.Add(" {"); typeCode.Add($" string global::Toolbelt.Blazor.I18nText.Interfaces.I18nTextTableHash.Hash => \"{hash}\";"); typeCode.Add(""); typeCode.Add($" string global::Toolbelt.Blazor.I18nText.Interfaces.I18nTextFallbackLanguage.FallBackLanguage => \"{options.FallBackLanguage}\";"); typeCode.Add(""); typeCode.Add(" public string this[string key] => global::Toolbelt.Blazor.I18nText.I18nTextExtensions.GetFieldValue(this, key);"); typeCode.Add(""); var is1stLine = true; foreach (var textKey in comilerItem.Type.Value.TextKeys) { if (!is1stLine) { typeCode.Add(""); } typeCode.Add($" /// <summary>\"{EscapeForXMLDocSummary(textTable[textKey])}\"</summary>"); typeCode.Add($" public string {textKey};"); is1stLine = false; } typeCode.Add(" }"); typeCode.Add("}"); saveCode?.Invoke(options, comilerItem, typeCode); }); }
public bool Compile(IEnumerable <I18nTextSourceFile> srcFiles, I18nTextCompilerOptions options) { return(Compile(srcFiles, options, beforeCompile: SweepTypeFilesShouldBePurged, saveCode: SaveTypeCodeToTypeFiles, CancellationToken.None)); }
/// <summary> /// Sweep old generated/should be purge type files. /// </summary> private static void SweepTypeFilesShouldBePurged(I18nTextCompilerOptions options, IEnumerable <I18nTextCompileItem> compilerItems) { SweepTypeFilesShouldBePurged(options, compilerItems, CancellationToken.None); }