private static void PrintLogo(LocBamlOptions option) { if (option == null || !option.HasNoLogo) { Console.WriteLine(StringLoader.Get("Msg_Copyright", GetAssemblyVersion())); } }
/// <summary> /// Write the localizable key-value pairs /// </summary> /// <param name="options"></param> internal static void Write(LocBamlOptions options) { Stream output = new FileStream(options.Output, FileMode.Create); var bamlStreamList = new InputBamlStreamList(options); using (var writer = options.CreateBamlResourceWriter(output)) { options.WriteLine(StringLoader.Get("WriteBamlValues")); for (int i = 0; i < bamlStreamList.Count; i++) { options.Write(" "); options.Write(StringLoader.Get("ProcessingBaml", bamlStreamList[i].Name)); // Search for comment file in the same directory. The comment file has the extension to be // "loc". string commentFile = Path.ChangeExtension(bamlStreamList[i].Name, "loc"); TextReader commentStream = null; try { if (File.Exists(commentFile)) { commentStream = new StreamReader(commentFile); } // create the baml localizer var mgr = new BamlLocalizer( bamlStreamList[i].Stream, new BamlLocalizabilityByReflection(options.Assemblies), commentStream ); // extract localizable resource from the baml stream BamlLocalizationDictionary dict = mgr.ExtractResources(); // write out each resource foreach (DictionaryEntry entry in dict) { writer.Write(bamlStreamList[i].Name, (BamlLocalizableResourceKey)entry.Key, (BamlLocalizableResource)entry.Value); } options.WriteLine(StringLoader.Get("Done")); } finally { if (commentStream != null) { commentStream.Close(); } } } // close all the baml input streams, output stream is closed by writer. bamlStreamList.Close(); } }
public static int Main(string[] args) { LocBamlOptions options; string errorMessage; GetCommandLineOptions(args, out options, out errorMessage); if (errorMessage != null) { // there are errors PrintLogo(options); Console.WriteLine(StringLoader.Get("ErrorMessage", errorMessage)); Console.WriteLine(); PrintUsage(); return(ErrorCode); // error } // at this point, we obtain good options. if (options == null) { // no option to process. Noop. return(SuccessCode); } //dispatcher = Dispatcher.CurrentDispatcher; PrintLogo(options); try { InitAssemblyResolver(options); // it is to parse if (options.ToParse) { ParseBamlResources(options); } else { GenerateBamlResources(options); } } catch (Exception e) { #if DEBUG throw e; #else Console.WriteLine(e.Message); return(ErrorCode); #endif } return(SuccessCode); }
public String Lookup(String strOpt, out bool bRequiresValue, out bool bCanHaveValue) { String strOptLower = strOpt.ToLower(CultureInfo.InvariantCulture); int i; bool bMatched = false; int iMatch = -1; // Compare option to stored list. for (i = 0; i < options.Length; i++) { // Exact matches always cause immediate termination of // the search if (strOptLower.Equals(options[i])) { bRequiresValue = requiresValue[i]; bCanHaveValue = canHaveValue[i]; return(options[i]); } // Check for potential match (the input word is a prefix // of the current stored option). if (options[i].StartsWith(strOptLower)) { // If we've already seen a prefix match then the // input word is ambiguous. if (bMatched) { throw new ArgumentException(StringLoader.Get("Err_AmbigousOption", strOpt)); } // Remember this partial match. bMatched = true; iMatch = i; } } // If we get here with bMatched set, we saw one and only one // partial match, so we've got a winner. if (bMatched) { bRequiresValue = requiresValue[iMatch]; bCanHaveValue = canHaveValue[iMatch]; return(options[iMatch]); } // Else the word doesn't match at all. throw new ArgumentException(StringLoader.Get("Err_UnknownOption", strOpt)); }
public CommandLine(String[] aArgs, String[] aValidOpts) { int i; // Keep a list of valid option names. validOptions = new Abbrevs(aValidOpts); // Temporary lists of raw arguments and options and their // associated values. var aArgList = new String[aArgs.Length]; var aOptList = new Option[aArgs.Length]; // Reset counters of raw arguments and option/value pairs found // so far. int iArg = 0; int iOpt = 0; // Iterate through words of command line. for (i = 0; i < aArgs.Length; i++) { // Check for option or raw argument. if (aArgs[i].StartsWith("/") || aArgs[i].StartsWith("-")) { String strVal = null; bool bRequiresValue; bool bCanHaveValue; // It's an option. Strip leading '/' or '-' and // anything after a value separator (':' or // '='). int iColon = aArgs[i].IndexOfAny(new[] { ':', '=' }); string strOpt = iColon == -1 ? aArgs[i].Substring(1) : aArgs[i].Substring(1, iColon - 1); // Look it up in the table of valid options (to // check it exists, get the full option name and // to see if an associated value is expected). strOpt = validOptions.Lookup(strOpt, out bRequiresValue, out bCanHaveValue); // Check that the user hasn't specified a value separator for an option // that doesn't take a value. if (!bCanHaveValue && (iColon != -1)) { throw new ApplicationException(StringLoader.Get("Err_NoValueRequired", strOpt)); } // Check that the user has put a colon if the option requires a value. if (bRequiresValue && (iColon == -1)) { throw new ApplicationException(StringLoader.Get("Err_ValueRequired", strOpt)); } // Go look for a value if there is one. if (bCanHaveValue && iColon != -1) { if (iColon == (aArgs[i].Length - 1)) { // No value separator, or // separator is at end of // option; look for value in // next command line arg. if (i + 1 == aArgs.Length) { throw new ApplicationException(StringLoader.Get("Err_ValueRequired", strOpt)); } if ((aArgs[i + 1].StartsWith("/") || aArgs[i + 1].StartsWith("-"))) { throw new ApplicationException(StringLoader.Get("Err_ValueRequired", strOpt)); } strVal = aArgs[i + 1]; i++; } else { // Value is in same command line // arg as the option, substring // it out. strVal = aArgs[i].Substring(iColon + 1); } } // Build the option value pair. aOptList[iOpt++] = new Option(strOpt, strVal); } else { // Command line word is a raw argument. aArgList[iArg++] = aArgs[i]; } } // Allocate the non-temporary arg and option lists at exactly // the right size. argList = new String[iArg]; optList = new Option[iOpt]; // Copy in the values we've calculated. Array.Copy(aArgList, argList, iArg); Array.Copy(aOptList, optList, iOpt); }
/// <summary> /// return true if the operation succeeded. /// otherwise, return false /// </summary> internal string CheckAndSetDefault() { // we validate the options here and also set default // if we can // Rule #1: One and only one action at a time // i.e. Can't parse and generate at the same time // Must do one of them if ((ToParse && ToGenerate) || (!ToParse && !ToGenerate)) { return(StringLoader.Get("MustChooseOneAction")); } // Rule #2: Must have an input if (string.IsNullOrEmpty(Input)) { return(StringLoader.Get("InputFileRequired")); } else { if (!File.Exists(Input)) { return(StringLoader.Get("FileNotFound", Input)); } string extension = Path.GetExtension(Input); // Get the input file type. if (string.Compare(extension, "." + FileType.BAML.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.BAML; } else if (string.Compare(extension, "." + FileType.RESOURCES.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.RESOURCES; } else if (string.Compare(extension, "." + FileType.DLL.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.DLL; } else if (string.Compare(extension, "." + FileType.EXE.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.EXE; } else { return(StringLoader.Get("FileTypeNotSupported", extension)); } } if (ToGenerate) { // Rule #3: before generation, we must have Culture string if (CultureInfo == null && InputType != FileType.BAML) { // if we are not generating baml, return(StringLoader.Get("CultureNameNeeded", InputType.ToString())); } // Rule #4: before generation, we must have translation file if (string.IsNullOrEmpty(Translations)) { return(StringLoader.Get("TranslationNeeded")); } else { string extension = Path.GetExtension(Translations); if (!File.Exists(Translations)) { return(StringLoader.Get("TranslationNotFound", Translations)); } else { if (string.Compare(extension, "." + FileType.CSV.ToString(), true, CultureInfo.InvariantCulture) == 0) { TranslationFileType = FileType.CSV; } else { TranslationFileType = FileType.TXT; } } } } // Rule #5: If the output file name is empty, we act accordingly if (string.IsNullOrEmpty(Output)) { // Rule #5.1: If it is parse, we default to [input file name].csv if (ToParse) { string fileName = Path.GetFileNameWithoutExtension(Input); Output = fileName + "." + FileType.CSV.ToString(); TranslationFileType = FileType.CSV; } else { // Rule #5.2: If it is generating, and the output can't be empty return(StringLoader.Get("OutputDirectoryNeeded")); } } else { // output isn't null, we will determind the Output file type // Rule #6: if it is parsing. It will be .csv or .txt. if (ToParse) { string fileName; string outputDir; if (Directory.Exists(Output)) { // the output is actually a directory name fileName = string.Empty; outputDir = Output; } else { // get the extension fileName = Path.GetFileName(Output); outputDir = Path.GetDirectoryName(Output); } // Rule #6.1: if it is just the output directory // we append the input file name as the output + csv as default if (string.IsNullOrEmpty(fileName)) { TranslationFileType = FileType.CSV; Output = outputDir + Path.DirectorySeparatorChar + Path.GetFileName(Input) + "." + TranslationFileType.ToString(); } else { // Rule #6.2: if we have file name, check the extension. string extension = Path.GetExtension(Output); // ignore case and invariant culture if (string.Compare(extension, "." + FileType.CSV.ToString(), true, CultureInfo.InvariantCulture) == 0) { TranslationFileType = FileType.CSV; } else { // just consider the output as txt format if it doesn't have .csv extension TranslationFileType = FileType.TXT; } } } else { // it is to generate. And Output should point to the directory name. if (!Directory.Exists(Output)) { return(StringLoader.Get("OutputDirectoryError", Output)); } } } // Rule #7: if the input assembly path is not null if (AssemblyPaths != null && AssemblyPaths.Count > 0) { Assemblies = new Assembly[AssemblyPaths.Count]; for (int i = 0; i < Assemblies.Length; i++) { string errorMsg = null; try { // load the assembly Assemblies[i] = Assembly.LoadFrom((string)AssemblyPaths[i]); } catch (ArgumentException argumentError) { errorMsg = argumentError.Message; } catch (BadImageFormatException formatError) { errorMsg = formatError.Message; } catch (FileNotFoundException fileError) { errorMsg = fileError.Message; } catch (PathTooLongException pathError) { errorMsg = pathError.Message; } catch (SecurityException securityError) { errorMsg = securityError.Message; } if (errorMsg != null) { return(errorMsg); // return error message when loading this assembly } } } // if we come to this point, we are all fine, return null error message return(null); }
private static void PrintUsage() { Console.WriteLine(StringLoader.Get("Msg_Usage")); }
/// <summary> /// get CommandLineOptions, return error message /// </summary> private static void GetCommandLineOptions(string[] args, out LocBamlOptions options, out string errorMessage) { CommandLine commandLine; try{ // "*" means the option must have a value. no "*" means the option can't have a value commandLine = new CommandLine(args, new string[] { "parse", // /parse for update "generate", // /generate for generate "*out", // /out for output .csv|.txt when parsing, for output directory when generating "*culture", // /culture for culture name "*translation", // /translation for translation file, .csv|.txt "*asmpath", // /asmpath, for assembly path to look for references (TODO: add asmpath support) "nologo", // /nologo for not to print logo "help", // /help for help "verbose", // /verbose for verbose output "*mode" // /mode:ms for MS comaptible mode ms|resx } ); } catch (ArgumentException e) { errorMessage = e.Message; options = null; return; } if (commandLine.NumArgs + commandLine.NumOpts < 1) { PrintLogo(null); PrintUsage(); errorMessage = null; options = null; return; } options = new LocBamlOptions(); options.Input = commandLine.GetNextArg(); Option commandLineOption; while ((commandLineOption = commandLine.GetNextOption()) != null) { if (commandLineOption.Name == "parse") { options.ToParse = true; } else if (commandLineOption.Name == "generate") { options.ToGenerate = true; } else if (commandLineOption.Name == "nologo") { options.HasNoLogo = true; } else if (commandLineOption.Name == "help") { // we print usage and stop processing PrintUsage(); errorMessage = null; options = null; return; } else if (commandLineOption.Name == "verbose") { options.IsVerbose = true; } else if (commandLineOption.Name == "mode") { if (commandLineOption.Value == "ms") { options.IsMsBamlMode = true; options.FileFormat = FileFormat.MsCsv; } else if (commandLineOption.Value == "resx") { options.FileFormat = FileFormat.Resx; } else { errorMessage = "Invalid value for 'mode' argument"; return; } } // the following ones need value else if (commandLineOption.Name == "out") { options.Output = commandLineOption.Value; } else if (commandLineOption.Name == "translation") { options.Translations = commandLineOption.Value; } else if (commandLineOption.Name == "asmpath") { if (options.AssemblyPaths == null) { options.AssemblyPaths = new ArrayList(); } options.AssemblyPaths.Add(commandLineOption.Value); } else if (commandLineOption.Name == "culture") { try { options.CultureInfo = new CultureInfo(commandLineOption.Value); } catch (ArgumentException e) { // Error errorMessage = e.Message; return; } } else { // something that we don't recognize errorMessage = StringLoader.Get("Err_InvalidOption", commandLineOption.Name); return; } } // we passed all the test till here. Now check the combinations of the options errorMessage = options.CheckAndSetDefault(); }
/// <summary> /// Generates localized Baml from translations /// </summary> internal void Generate() { // base on the input, we generate differently switch (options.InputType) { case FileType.BAML: { // input file name string bamlName = Path.GetFileName(options.Input); // outpuf file name is Output dir + input file name string outputFileName = GetOutputFileName(); // construct the full path string fullPathOutput = Path.Combine(options.Output, outputFileName); options.Write(StringLoader.Get("GenerateBaml", fullPathOutput)); using (Stream input = File.OpenRead(options.Input)) { using (Stream output = new FileStream(fullPathOutput, FileMode.Create)) { GenerateBamlStream(input, output, GetDictionaries(bamlName)); } } options.WriteLine(StringLoader.Get("Done")); break; } case FileType.RESOURCES: { string outputFileName = GetOutputFileName(); string fullPathOutput = Path.Combine(options.Output, outputFileName); using (Stream input = File.OpenRead(options.Input)) { using (Stream output = File.OpenWrite(fullPathOutput)) { // create a Resource reader on the input; IResourceReader reader = new ResourceReader(input); // create a writer on the output; IResourceWriter writer = new ResourceWriter(output); GenerateResourceStream( options.Input, // resources name reader, // resource reader writer); // resource writer reader.Close(); // now generate and close writer.Generate(); writer.Close(); } } options.WriteLine(StringLoader.Get("DoneGeneratingResource", outputFileName)); break; } case FileType.EXE: case FileType.DLL: { GenerateAssembly(); break; } default: { Debug.Assert(false, "Can't generate to this type"); break; } } }
//-------------------------------------------------- // The function follows Managed code parser // implementation. in the future, maybe they should // share the same code //-------------------------------------------------- private void GenerateAssembly() { // there are many names to be used when generating an assembly string sourceAssemblyFullName = options.Input; // source assembly full path string outputAssemblyDir = options.Output; // output assembly directory string outputAssemblyLocalName = GetOutputFileName(); // output assembly name string moduleLocalName = GetAssemblyModuleLocalName(outputAssemblyLocalName); // the module name within the assmbly // get the source assembly Assembly srcAsm = Assembly.LoadFrom(sourceAssemblyFullName); // obtain the assembly name AssemblyName targetAssemblyNameObj = srcAsm.GetName(); // store the culture info of the source assembly CultureInfo srcCultureInfo = targetAssemblyNameObj.CultureInfo; // update it to use it for target assembly targetAssemblyNameObj.Name = Path.GetFileNameWithoutExtension(outputAssemblyLocalName); targetAssemblyNameObj.CultureInfo = options.CultureInfo; // we get a assembly builder AssemblyBuilder targetAssemblyBuilder = Thread.GetDomain().DefineDynamicAssembly( targetAssemblyNameObj, // name of the assembly AssemblyBuilderAccess.RunAndSave, // access rights outputAssemblyDir // storage dir ); // we create a module builder for embeded resource modules ModuleBuilder moduleBuilder = targetAssemblyBuilder.DefineDynamicModule( moduleLocalName, outputAssemblyLocalName ); options.WriteLine(StringLoader.Get("GenerateAssembly")); // now for each resource in the assembly foreach (string resourceName in srcAsm.GetManifestResourceNames()) { // get the resource location for the resource ResourceLocation resourceLocation = srcAsm.GetManifestResourceInfo(resourceName).ResourceLocation; // if this resource is in another assemlby, we will skip it if ((resourceLocation & ResourceLocation.ContainedInAnotherAssembly) != 0) { continue; // in resource assembly, we don't have resource that is contained in another assembly } // gets the neutral resource name, giving it the source culture info string neutralResourceName = GetNeutralResModuleName(resourceName, srcCultureInfo); // gets the target resource name, by giving it the target culture info string targetResourceName = GetCultureSpecificResourceName(neutralResourceName, options.CultureInfo); // resource stream Stream resourceStream = srcAsm.GetManifestResourceStream(resourceName); // see if it is a .resources if (neutralResourceName.ToLower(CultureInfo.InvariantCulture).EndsWith(".resources")) { // now we think we have resource stream // get the resource writer IResourceWriter writer; // check if it is a embeded assembly if ((resourceLocation & ResourceLocation.Embedded) != 0) { // gets the resource writer from the module builder writer = moduleBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // resource description ResourceAttributes.Public // visibilty of this resource to other assembly ); } else { // it is a standalone resource, we get the resource writer from the assembly builder writer = targetAssemblyBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // description targetResourceName, // file name to save to ResourceAttributes.Public // visibility of this resource to other assembly ); } // get the resource reader IResourceReader reader = new ResourceReader(resourceStream); // generate the resources GenerateResourceStream(resourceName, reader, writer); // we don't call writer.Generate() or writer.Close() here // because the AssemblyBuilder will call them when we call Save() on it. } else { // else it is a stand alone untyped manifest resources. string extension = Path.GetExtension(targetResourceName); string fullFileName = Path.Combine(outputAssemblyDir, targetResourceName); // check if it is a .baml, case-insensitive if (string.Compare(extension, ".baml", true, CultureInfo.InvariantCulture) == 0) { // try to localized the the baml // find the resource dictionary var curDictionaries = GetDictionaries(resourceName); // if it is null, just create an empty dictionary. if (!curDictionaries.IsEmpty) { // it is a baml stream using (Stream output = File.OpenWrite(fullFileName)) { options.Write(" "); options.WriteLine(StringLoader.Get("GenerateStandaloneBaml", fullFileName)); GenerateBamlStream(resourceStream, output, curDictionaries); options.WriteLine(StringLoader.Get("Done")); } } else { // can't find localization of it, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } } else { // it is an untyped resource stream, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } // now add this resource file into the assembly targetAssemblyBuilder.AddResourceFile( targetResourceName, // resource name targetResourceName, // file name ResourceAttributes.Public // visibility of the resource to other assembly ); } } // at the end, generate the assembly targetAssemblyBuilder.Save(outputAssemblyLocalName); options.WriteLine(StringLoader.Get("DoneGeneratingAssembly")); }
private void GenerateResourceStream( string resourceName, // the name of the .resources file IResourceReader reader, // the reader for the .resources IResourceWriter writer // the writer for the output .resources ) { options.WriteLine(StringLoader.Get("GenerateResource", resourceName)); // enumerate through each resource and generate it foreach (DictionaryEntry entry in reader) { var name = entry.Key as string; object resourceValue = null; // See if it looks like a Baml resource if (BamlStream.IsResourceEntryBamlStream(name, entry.Value)) { Stream targetStream = null; options.Write(" "); options.Write(StringLoader.Get("GenerateBaml", name)); // grab the localizations available for this Baml string bamlName = BamlStream.CombineBamlStreamName(resourceName, name, options.IsMsBamlMode); Dictionaries localizations = GetDictionaries(bamlName); if (!localizations.IsEmpty) { targetStream = new MemoryStream(); // generate into a new Baml stream GenerateBamlStream((Stream)entry.Value, targetStream, localizations); } options.WriteLine(StringLoader.Get("Done")); // sets the generated object to be the generated baml stream resourceValue = targetStream; } if (resourceValue == null) { // // The stream is not localized as Baml yet, so we will make a copy of this item into // the localized resources // // We will add the value as is if it is serializable. Otherwise, make a copy resourceValue = entry.Value; object[] serializableAttributes = resourceValue.GetType().GetCustomAttributes(typeof(SerializableAttribute), true); if (serializableAttributes.Length == 0) { // The item returned from resource reader is not serializable // If it is Stream, we can wrap all the values in a MemoryStream and // add to the resource. Otherwise, we had to skip this resource. var resourceStream = resourceValue as Stream; if (resourceStream != null) { var buffer = new byte[resourceStream.Length]; resourceStream.Read(buffer, 0, buffer.Length); Stream targetStream = new MemoryStream(buffer); resourceValue = targetStream; } } } if (resourceValue != null) { writer.AddResource(name, resourceValue); } } }