/// <summary> /// Prompts for environment by looking into spreadsheet and listing them out /// to the console. /// </summary> /// <param name="source">The data source.</param> /// <param name="context">The preprocessing context.</param> /// <returns></returns> private static string PromptForEnvironment(DataSource source, PreprocessingContext context) { SettingsLoader loader = new SettingsLoader(context); Console.WriteLine("Environment name was not passed."); Console.WriteLine(""); ConsoleUtils.WriteLine(ConsoleColor.Cyan, "These are the environment columns found in the spreadsheet:"); List <string> environments = loader.GetEnvironments(source); for (int j = 0; j < environments.Count; j++) { ConsoleUtils.WriteLine(ConsoleColor.Cyan, string.Format(" {0} - {1}", j, environments[j])); } ConsoleUtils.Write(ConsoleColor.Cyan, "Type the environment # to use and press Enter: "); string environmentIndexString = Console.ReadLine(); int environmentIndex = -1; if (int.TryParse(environmentIndexString, out environmentIndex)) { return(environments[environmentIndex]); } return(null); }
/// <summary> /// Parse any command line properties, allowing them /// to override the file base ones /// </summary> /// <param name="context">Preprocessing context</param> /// <param name="array">ArrayList read from command line</param> public void AddPropertiesFromArrayList(PreprocessingContext context, ArrayList array) { for (int i = 0; i < array.Count; i++) { string property = (string)array[i]; if (null != property && property.Length > 0) { string propertyName = null; string propertyValue = ""; int equalPos = -1; if (context.IsDynamicProperty(property)) { equalPos = property.LastIndexOf('='); } else { equalPos = property.IndexOf('='); } if (-1 == equalPos) { propertyName = property; } else { propertyName = property.Substring(0, equalPos); propertyValue = property.Substring(equalPos + 1); } Add(propertyName, propertyValue); } } }
/// <summary> /// Deploy an XML file using preprocessor. /// </summary> /// <param name="context">Preprocessing Context</param> public int Preprocess(PreprocessingContext context) { // load the file into a string string source = FileUtils.LoadFile(context.SourceFile); // add all of the built-in properties like _current_dir context.AddBuiltInProperties(); // pre-load embedded defines in case there are any defines used in includes ExtractDefines(context, source); // pull-in any externally included files string dest = ProcessIncludes(context, source); // again scan for embedded defines in case the includes contained them ExtractDefines(context, dest); // process the entire file dest = Process(context, dest); if (context.ValidateSettingsExist && context.Errors.Count > 0) { // don't write file, just return error code return(2); } else { if (context.ValidateXmlWellFormed) { try { XmlDocument wellFormednessCheckingDocument = new XmlDocument(); wellFormednessCheckingDocument.LoadXml(dest); } catch (XmlException e) { string errorFile = context.DestinationFile + ".error"; FileUtils.WriteFile(errorFile, dest); ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorNotWellFormed, context.SourceFile); errorInfo.Message = string.Format("Output was not well-formed: {0}, A copy of the file that was not well-formed was saved to {1}.", e.Message, errorFile); context.Errors.Add(errorInfo); return(1); } } // Only write out the file if the destination is different // than the source or if changes were made if (!context.DestinationFile.Equals(context.SourceFile) || !source.Equals(dest)) { FileUtils.WriteFile(context.DestinationFile, dest); } } return(0); }
/// <summary> /// Remove macro braces (example: ${...}) from a section of content. /// </summary> /// <param name="context">The preprocessing context.</param> /// <param name="content">The content from which to remove the macro braces.</param> /// <returns>Content without macro braces.</returns> private string RemoveMacro(PreprocessingContext context, string content) { string result = null; if (null != content) { result = content.Replace(context.TokenStart, ""); result = result.Replace(context.TokenEnd, ""); result = result.Trim(); } return(result); }
/// <summary> /// Resolves content that could possibly contain macros /// </summary> /// <param name="context">The preprocessing context.</param> /// <param name="content">The content that could contain macros.</param> /// <exception cref="UndefinedSettingException">Throws this exception if it encounters an undefined setting</exception> /// <returns>the resolved content</returns> public string ResolveContent(PreprocessingContext context, string content) { if (!string.IsNullOrEmpty(content)) { bool isSameTokenUsedForStartAndEnd = context.TokenStart.Equals(context.TokenEnd, StringComparison.OrdinalIgnoreCase); // Look for start of tokens, order depends on type of token used int macroPosition = -1; // Evaluate from back to front if tokens are not equal // this enables nested tokens such as this: ${PROPERTY_${MACHINE}} // Evaluate from front to back if start and end tokens are equal...nested properties with // custom tokens that match is not supported. You can't do this: #PROPERTY_#MACHINE## if (!isSameTokenUsedForStartAndEnd) { macroPosition = content.LastIndexOf(context.TokenStart); } else { macroPosition = content.IndexOf(context.TokenStart); } while (macroPosition > -1) { int endMacroPosition = content.IndexOf(context.TokenEnd, macroPosition + context.TokenStart.Length); string macro = content.Substring(macroPosition, endMacroPosition - macroPosition + 1); string key = macro.Substring(context.TokenStart.Length, macro.Length - (context.TokenStart.Length + context.TokenEnd.Length)).Trim(); PreprocessingProperty property = this[key]; if (null == property) { throw new UndefinedSettingException(string.Format("{0} was not defined", key), key); } string val = property.Value; content = content.Replace(macro, val); if (!isSameTokenUsedForStartAndEnd) { macroPosition = content.LastIndexOf(context.TokenStart); } else { macroPosition = content.IndexOf(context.TokenStart); } } } return(content); }
/// <summary> /// Reports the use count. /// </summary> /// <param name="cl">The cl.</param> /// <param name="context">The context.</param> private static void ReportUseCount(CommandLine cl, PreprocessingContext context) { if (!string.IsNullOrEmpty(cl.countReportFile)) { StringBuilder sb = new StringBuilder(); sb.Append("Property, Count\r\n"); foreach (var property in context.Properties) { if (!property.Key.StartsWith("_")) { sb.Append(string.Format("{0}, {1}\r\n", property.Key, property.UseCount)); } } FileUtils.WriteFile(cl.countReportFile, sb.ToString()); } }
/// <summary> /// Dumps the environment names from the data source to the console. /// </summary> /// <param name="context">The preprocessing context.</param> /// <returns></returns> private static int DumpEnvironments(PreprocessingContext context) { int exitCode = 0; try { DataSource firstDataSource = context.DataSources[0]; SettingsLoader loader = new SettingsLoader(context); DataTable settingsTable = loader.LoadDataTableFromDataSource(firstDataSource); List <string> environments = loader.GetEnvironmentsFromDataTable(settingsTable); foreach (string environment in environments) { if (!string.IsNullOrEmpty(context.PropertyToExtract)) { PreprocessingProperties properties = new PreprocessingProperties(context.FixFalse); loader.LoadSettingsFromDataTable(settingsTable, context.Properties, environment); PreprocessingProperty property = context.Properties[context.PropertyToExtract]; if (null != property) { string resolvedPropertyValue = context.ResolveContent(property.Value); if (!string.IsNullOrEmpty(resolvedPropertyValue)) { Console.WriteLine(resolvedPropertyValue); } } } else { Console.WriteLine(environment); } } } catch (Exception e) { ConsoleUtils.WriteLine(ConsoleColor.Red, e.Message); exitCode = 1; } return(exitCode); }
/// <summary> /// Extracts the #defines from the document /// </summary> /// <param name="context">The Preprocessing context.</param> /// <param name="content">The content.</param> private void ExtractDefines(PreprocessingContext context, string content) { for (Match matchDefine = regexDefine.Match(content); matchDefine.Success; matchDefine = matchDefine.NextMatch()) { Group expressionGroup = matchDefine.Groups["expression"]; Group valueGroup = matchDefine.Groups["value"]; if (null != expressionGroup && null != valueGroup && !string.IsNullOrEmpty(expressionGroup.Value) && !string.IsNullOrEmpty(valueGroup.Value)) { string expression = expressionGroup.Value.Trim(); string value = valueGroup.Value.Trim(); context.Properties.Add(expression, value); } } }
/// <summary> /// Processes the dynamically bound properties. /// </summary> /// <remarks> /// This is a fairly inefficient implementation for the first cut, it would be /// nicer if a buffer could be reused to avoid reallocating huge strings for /// every single property, especially in the case of XML. /// </remarks> /// <param name="context">The Preprocessing context.</param> /// <param name="content">The content.</param> /// <returns>The buffer with any dynamically bound properties replaced</returns> private string ProcessDynamicallyBoundProperties(PreprocessingContext context, string content) { string resultBuffer = content; foreach (PreprocessingProperty property in context.Properties) { if (context.IsDynamicProperty(property.Key)) { IDynamicResolver resolver = property.GetDynamicResolver(context); if (resolver.ShouldProcess(context.SourceFile)) { string replacementValue = ResolveProperties(property.Value, context); resultBuffer = resolver.Replace(resultBuffer, replacementValue); } } } return(resultBuffer); }
/// <summary> /// Reads the settings from files. /// </summary> /// <param name="context">The preprocessing context.</param> /// <returns>Error code</returns> private static int ReadSettings(PreprocessingContext context) { // read data source settings files int exitCode = ReadSettingsFromDataSources(context); if (0 != exitCode) { return(exitCode); } // read loose settings files (moved after data sources to allow overriding) exitCode = ReadEnvironmentSettingsFile(context); if (0 != exitCode) { return(exitCode); } return(exitCode); }
/// <summary> /// Resolves an expression /// </summary> /// <param name="expression">The function.</param> /// <param name="context">The preprocessing context.</param> /// <returns>The resulting value</returns> private string ResolveExpression(string expression, PreprocessingContext context) { string val = ""; DynamicEvaluator evaluator = new DynamicEvaluator(); try { val = evaluator.EvaluateToString(context, expression); } catch (Exception ex) { val = "<!-- " + expression + " not properly formed -->"; ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorNotWellFormed, context.SourceFile); errorInfo.Message = string.Format("The expression '{0}' was not properly formed. {1}", expression, ex.ToString()); context.Errors.Add(errorInfo); } return(val); }
/// <summary> /// Dumps an individual property from the data source to the console. /// </summary> /// <param name="context">The preprocessing context.</param> /// <returns></returns> private static int DumpProperty(PreprocessingContext context) { int exitCode = 0; try { PreprocessingProperty property = context.Properties[context.PropertyToExtract]; if (null != property) { string resolvedValue = context.ResolveContent(property.Value); if (!string.IsNullOrEmpty(resolvedValue)) { if (!string.IsNullOrEmpty(context.Delimiters)) { foreach (string s in resolvedValue.Split(context.Delimiters.ToCharArray())) { Console.WriteLine(s.Trim()); } } else { Console.WriteLine(resolvedValue); } } } } catch (Exception e) { ConsoleUtils.WriteLine(ConsoleColor.Red, e.Message); exitCode = 1; } return(exitCode); }
public static int Main(string[] args) { int exitCode = 0; // be nice & set console color back in the case of a Ctrl-C ConsoleColor originalColor = Console.ForegroundColor; Console.CancelKeyPress += delegate { Console.ForegroundColor = originalColor; }; // Parse the command line and show help or version or error CommandLine cl = new CommandLine(); if (!cl.ParseAndContinue(args)) { exitCode = 1; return(exitCode); } // Make sure argument combinations are valid if (!cl.ValidateArguments()) { exitCode = 1; return(exitCode); } // Output logo if they didn't ask to turn it off, and they aren't extracting something bool isExtractingProperty = !string.IsNullOrEmpty(cl.property); if (!cl.noLogo && !cl.list && !isExtractingProperty) { Console.WriteLine(cl.GetCommandLineLogo()); } cl.NormalizeAllFileArrays(); PreprocessingContext context = cl.CreatePreprocessingContext(); if (cl.list) { return(DumpEnvironments(context)); } // Do not prompt for input if extracting a property if (isExtractingProperty) { context.QuietMode = true; } // read settings files exitCode = ReadSettings(context); if (0 == exitCode && !string.IsNullOrEmpty(cl.environmentFile)) { Console.WriteLine("Writing selected environment \"{0}\" to {1}", context.EnvironmentName, cl.environmentFile); FileUtils.WriteFile(cl.environmentFile, context.EnvironmentName); return(exitCode); } if (0 == exitCode) { // Add properties from command line last so they override everything else context.Properties.AddPropertiesFromArrayList(context, cl.define); if (isExtractingProperty) { return(DumpProperty(context)); } else { XmlPreprocessor preprocessor = new XmlPreprocessor(); // loop through input files for (int i = 0; i < cl.input.Count; i++) { context.SourceFile = cl.input[i] as string; if (null != context.SourceFile) { context.SourceFile = context.SourceFile.Trim(); } if (!string.IsNullOrEmpty(context.SourceFile)) { if (!File.Exists(context.SourceFile)) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorFileNotFound, context.SourceFile); errorInfo.Message = string.Format("Input file was not found: \"{0}\"", context.SourceFile); context.Errors.Add(errorInfo); exitCode = 1; break; } context.DestinationFile = null; if (i < cl.output.Count) { context.DestinationFile = cl.output[i] as string; } // If destination file was not specified, use input file if (string.IsNullOrEmpty(context.DestinationFile)) { context.DestinationFile = context.SourceFile; ConsoleUtils.WriteLine(ConsoleColor.Cyan, string.Format("Preprocessing \"{0}\"...", context.SourceFile)); } else { ConsoleUtils.WriteLine(ConsoleColor.Cyan, string.Format("Preprocessing \"{0}\" to \"{1}\"...", context.SourceFile, context.DestinationFile)); } try { exitCode = preprocessor.Preprocess(context); if (0 != exitCode) { break; } } catch (Exception e) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorException, context.SourceFile); errorInfo.Message = e.Message; context.Errors.Add(errorInfo); exitCode = 1; break; } } } } } ReportUseCount(cl, context); ReportErrorsAndWarnings(context.Errors); return(exitCode); }
/// <summary> /// Replace any macros with their property value. /// </summary> /// <param name="content">Content in which to replace all macros.</param> /// <param name="context">The preprocessing context.</param> /// <param name="overriddenValues">The value to insert.</param> /// <returns>The content with macros replaced with corresponding property values.</returns> private string ResolveProperties(string content, PreprocessingContext context, IDictionary <string, string> overriddenValues) { bool containedEscapedMacros = false; const string startEscapedMacro = "{6496D0A7-21B9-4603-A2E5-43C64FCD435E{"; const string endEscapedMacro = "}6496D0A7-21B9-4603-A2E5-43C64FCD435E}"; bool isSameTokenUsedForStartAndEnd = context.TokenStart.Equals(context.TokenEnd, StringComparison.OrdinalIgnoreCase); if (null != content && null != context.Properties) { // Look for start of tokens, order depends on type of token used int macroPosition = -1; // Evaluate from back to front if tokens are not equal // this enables nested tokens such as this: ${PROPERTY_${MACHINE}} // Evaluate from front to back if start and end tokens are equal...nested properties with // custom tokens that match is not supported. You can't do this: #PROPERTY_#MACHINE## if (!isSameTokenUsedForStartAndEnd) { macroPosition = content.LastIndexOf(context.TokenStart); } else { macroPosition = content.IndexOf(context.TokenStart); } while (macroPosition > -1) { int endMacroPosition = content.IndexOf(context.TokenEnd, macroPosition + context.TokenStart.Length); string macro = content.Substring(macroPosition, endMacroPosition - macroPosition + 1); string key = macro.Substring(context.TokenStart.Length, macro.Length - (context.TokenStart.Length + context.TokenEnd.Length)).Trim(); string val = null; // if the key starts out with "script=" treat it as an expression if (key.Length > 6 && key.StartsWith("script", StringComparison.OrdinalIgnoreCase) && key.Substring(6).Trim().StartsWith("=")) { key = key.Substring(6).Trim().Substring(1).Trim(); if (!string.IsNullOrEmpty(key)) { val = ResolveExpression(key, context); } } else if (key.Length > 8 && key.StartsWith("registry", StringComparison.OrdinalIgnoreCase) && key.Substring(8).Trim().StartsWith("=")) { key = key.Substring(8).Trim().Substring(1).Trim(); if (!string.IsNullOrEmpty(key)) { val = RegistryEvaluator.GetValue(key); } } else { if (null != overriddenValues && overriddenValues.ContainsKey(key)) { val = overriddenValues[key]; } else { PreprocessingProperty property = null; // Implementation of fallback properties. // Go through the semicolon delimited list // of properties looking for the first one that exists // ex: ${PROPERTY_ABC;PROPERTY_DEF;PROPERTY} // useful for machine-specific configuration // when coupled with nested properties like this: // ${PROPERTY_${_machine_name};PROPERTY} string[] keys = key.Split(';'); foreach (string keyPart in keys) { string keyPartTrimmed = keyPart.Trim(); if (!string.IsNullOrEmpty(keyPartTrimmed)) { property = context.Properties[keyPartTrimmed]; if (null != property) { break; } } } if (null != property) { val = property.Value; if (context.CountUsage) { int useCount = CountOccurrences(content, macro); property.UseCount = property.UseCount + useCount; } } else { //Pavan added code here.. val = "$${{" + key + "}}"; ConsoleUtils.WriteLine(ConsoleColor.Yellow, string.Format("The setting named '{0}' was not defined.", key)); //Instead of erroring out, keep macro as value and write to warning. /* * val = "<!-- " + key + " not defined -->"; * * if (context.ValidateSettingsExist) * { * ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorMissingToken, context.SourceFile); * errorInfo.Message = string.Format("The setting named '{0}' was not defined.", key); * context.Errors.Add(errorInfo); * }*/ } } } // Enable Escaping if (!string.IsNullOrEmpty(val)) { int escapedMacroStartPos = val.IndexOf(context.TokenStartEscaped); int escapedMacroEndPos = val.IndexOf(context.TokenEndEscaped); if (escapedMacroStartPos > -1 && escapedMacroEndPos > escapedMacroStartPos) { val = val.Replace(context.TokenStartEscaped, startEscapedMacro).Replace(context.TokenEndEscaped, endEscapedMacro); containedEscapedMacros = true; } } content = content.Replace(macro, val); if (!isSameTokenUsedForStartAndEnd) { macroPosition = content.LastIndexOf(context.TokenStart); } else { macroPosition = content.IndexOf(context.TokenStart); } } } if (containedEscapedMacros) { content = content.Replace(startEscapedMacro, context.TokenStart).Replace(endEscapedMacro, context.TokenEnd); } return(content); }
/// <summary> /// Reads from spreadsheet. /// </summary> /// <param name="source">The settings source.</param> /// <param name="context">the context.</param> public void LoadFromDataSource(DataSource source, PreprocessingContext context) { SettingsLoader loader = new SettingsLoader(context); loader.LoadSettingsFromDataSource(source, this, context.EnvironmentName); }
/// <summary> /// Loads settings from the environment settings files. /// </summary> /// <param name="context">The preprocessing context.</param> /// <returns>Error code</returns> private static int ReadEnvironmentSettingsFile(PreprocessingContext context) { int exitCode = 0; bool isExtractingProperty = !string.IsNullOrEmpty(context.PropertyToExtract); // read settings file // if settings file was specified, but is empty or not found, prompt for it if (null != context.SettingsFiles && context.SettingsFiles.Count > 0) { for (int i = 0; i < context.SettingsFiles.Count; i++) { string settingsFile = context.SettingsFiles[i] as string; if (null != settingsFile) { settingsFile = settingsFile.Trim(); if (!FileUtils.IsHttpUrl(settingsFile) && !File.Exists(settingsFile)) { Console.WriteLine(string.Format("\nSettings XML file not found: \"{0}\"", settingsFile)); if (!context.QuietMode) { while (true) { Console.WriteLine("Enter path to settings XML file, leave blank if no settings are required."); Console.Write("Settings XML file: "); settingsFile = Console.ReadLine().Trim(); if (settingsFile.Length == 0) { break; } if (File.Exists(settingsFile)) { break; } Console.WriteLine(string.Format("Settings XML file not found: \"{0}\"", settingsFile)); } } } else { if (!isExtractingProperty) { Console.WriteLine(string.Format("Settings XML file: \"{0}\"", settingsFile)); } } if (null != settingsFile && settingsFile.Length > 0) { try { context.Properties.ReadFromFile(settingsFile, context.EnvironmentName); } catch (XmlException xe) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorException, context.SourceFile); errorInfo.Message = string.Format("Error parsing {0}: {1} at line {2}", settingsFile, xe.Message, xe.LineNumber); context.Errors.Add(errorInfo); exitCode = 1; break; } catch (Exception e) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorException, context.SourceFile); errorInfo.Message = string.Format("Error parsing {0}: {1}", settingsFile, e.Message); context.Errors.Add(errorInfo); exitCode = 1; break; } } } } } return(exitCode); }
/// <summary> /// Process the body of a macro expression. /// </summary> /// <param name="context">The preprocessing context.</param> /// <param name="keyword">Keyword (#if or #ifdef).</param> /// <param name="condition">Condition to test.</param> /// <param name="body">Body of macro expression.</param> /// <returns>Returns the preprocessed result of the macro expression.</returns> private string ProcessBody(PreprocessingContext context, string keyword, string condition, string body) { string ifbody = null; string elsebody = null; bool hasElse = false; bool conditionTrue = false; //////////// //for (Match matchElif = regexElif.Match(body); // matchElif.Success; // matchElif = matchElif.NextMatch()) //{ //} //////////// Match matchElse = regexElse.Match(body); if (matchElse.Success) { hasElse = true; ifbody = body.Substring(0, matchElse.Index); elsebody = body.Substring(matchElse.Index + matchElse.Length); } else { ifbody = body; } string result = ""; conditionTrue = false; if (keyword.EndsWith("ifdef")) { conditionTrue = (!string.IsNullOrEmpty(condition) && context.Properties.ContainsKey(condition)); } else { DynamicEvaluator evaluator = new DynamicEvaluator(); conditionTrue = evaluator.EvaluateToBool(context, condition); } if (context.PreserveMarkup) { string whitespacePrefix = ""; for (int i = 0; i < ifbody.Length; i++) { if (!Char.IsWhiteSpace(ifbody[i])) { whitespacePrefix = ifbody.Substring(0, i); break; } } string commentedOutIfBody = RemoveComment(ifbody, false).Trim(); if (null != commentedOutIfBody && commentedOutIfBody.Length > 0) { if (commentedOutIfBody.IndexOf('\n') > -1) // multi-line { result += whitespacePrefix + "<!--" + whitespacePrefix + commentedOutIfBody + whitespacePrefix + "-->"; } else // single line { result += whitespacePrefix + "<!-- " + commentedOutIfBody + " -->"; } } if (!hasElse) // always add an else { if (keyword.StartsWith("#")) { result += whitespacePrefix + "<!-- #else -->"; } else { result += whitespacePrefix + "<!-- else -->"; } } else { result += body.Substring(matchElse.Index, matchElse.Length); } } if (conditionTrue) { if (context.PreserveMarkup && !hasElse) { result += ResolveProperties(RemoveComment(ifbody, true), context); } if (!(context.PreserveMarkup && !hasElse)) { result += ResolveProperties(RemoveComment(ifbody, true), context); } if (context.PreserveMarkup && hasElse && 0 == ifbody.Trim().Length) { result += "<!-- " + RemoveComment(elsebody, false) + " -->"; } } else { if (hasElse) { result += elsebody; } } return(result); }
/// <summary> /// Process the file. /// </summary> /// <param name="context">The preprocessing context.</param> /// <param name="content">Contents of a file as a string.</param> /// <returns>Returns the file contents with preprocessing applied.</returns> private string Process(PreprocessingContext context, string content) { StringBuilder result = new StringBuilder(); int offset = 0; if (context.NoDirectives) { result.Append(ResolveProperties(content, context)); } else { for (Match matchIfdef = regexIfdef.Match(content); matchIfdef.Success; matchIfdef = matchIfdef.NextMatch()) { if (context.PreserveMarkup) { result.Append(content.Substring(offset, (matchIfdef.Index + matchIfdef.Length) - offset)); } else { result.Append(content.Substring(offset, matchIfdef.Index - offset)); } offset = matchIfdef.Index + matchIfdef.Length; Match matchEndif = regexEndif.Match(content, offset); if (matchEndif.Success) { string condition = RemoveMacro(context, matchIfdef.Groups["condition"].Value); string keyword = RemoveMacro(context, matchIfdef.Groups["keyword"].Value); string body = content.Substring(offset, matchEndif.Index - offset); // Check to see if there is another ifdef in the body, // if so, it is an error Match matchErroneousIfdef = regexIfdef.Match(body); if (matchErroneousIfdef.Success) { throw new Exception("Comments are malformed, endif missing."); } offset = matchEndif.Index + matchEndif.Length; result.Append(ProcessBody(context, keyword, condition, body)); if (context.PreserveMarkup) { result.Append(content.Substring(matchEndif.Index, matchEndif.Length)); } } else { throw new Exception("Comments are malformed, no endif found."); } } result.Append(content.Substring(offset)); } return(ProcessDynamicallyBoundProperties(context, result.ToString())); }
/// <summary> /// Processes the #includes /// </summary> /// <param name="context">The Preprocessing context.</param> /// <param name="content">The content.</param> private string ProcessIncludes(PreprocessingContext context, string content) { while (true) { Match matchInclude = regexInclude.Match(content); if (!matchInclude.Success) { break; } Group fileGroup = matchInclude.Groups["file"]; Group xpathGroup = matchInclude.Groups["xpath"]; if (null != fileGroup && !string.IsNullOrEmpty(fileGroup.Value)) { string file = fileGroup.Value.Trim(); // replace any macros in file name string resolvedFile = ResolveProperties(file, context, null); if (context.Errors.Count > 0) { return(content); } // turn relative paths into fully qualified relative to the location of source file if (!Path.IsPathRooted(resolvedFile)) { resolvedFile = Path.Combine(Path.GetDirectoryName(context.SourceFile), resolvedFile); } if (!File.Exists(resolvedFile)) { throw new FileNotFoundException(string.Format("Could not find file {0}", resolvedFile), resolvedFile); } string source = ""; if (null != xpathGroup && !string.IsNullOrEmpty(xpathGroup.Value)) { string xpathUnresolved = xpathGroup.Value.Trim(); // replace any macros in xpath string xpath = ResolveProperties(xpathUnresolved, context, null); if (context.Errors.Count > 0) { return(content); } source = GetXmlContentFromIncludeFile(resolvedFile, xpath); } else { // Load file source = FileUtils.LoadFile(resolvedFile); } // Insert contents content = content.Substring(0, matchInclude.Index) + source + content.Substring(matchInclude.Index + matchInclude.Length); } } return(content); }
/// <summary> /// Gets the dynamic resolver. /// </summary> /// <param name="context">The preeprocessing context.</param> /// <returns></returns> public IDynamicResolver GetDynamicResolver(PreprocessingContext context) { return(DynamicBindingExpression.Parse(context, Key) as IDynamicResolver); }
/// <summary> /// Reads the settings from data sources. /// </summary> /// <param name="context">The preprocessing context.</param> /// <returns>Error code</returns> private static int ReadSettingsFromDataSources(PreprocessingContext context) { int exitCode = 0; bool isExtractingProperty = !string.IsNullOrEmpty(context.PropertyToExtract); // read settings file // if data sources were specified, but environment was not, prompt for it if (null != context.DataSources && context.DataSources.Count > 0) { foreach (DataSource dataSource in context.DataSources) { if (!dataSource.Exists) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorFileNotFound, context.SourceFile); errorInfo.Message = string.Format("Settings data source not found: \"{0}\"", dataSource); context.Errors.Add(errorInfo); exitCode = 1; break; } else { if (!isExtractingProperty) { Console.WriteLine(string.Format("Settings data source: \"{0}\"", dataSource)); } } if (!string.IsNullOrEmpty(dataSource.Path)) { if (!context.QuietMode && string.IsNullOrEmpty(context.EnvironmentName)) { context.EnvironmentName = PromptForEnvironment(dataSource, context); } if (string.IsNullOrEmpty(context.EnvironmentName)) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorException, context.SourceFile); errorInfo.Message = string.Format("Error loading settings from {0}, environment name was not supplied.", dataSource); context.Errors.Add(errorInfo); exitCode = 1; break; } else { try { context.Properties.LoadFromDataSource(dataSource, context); } catch (XmlException xe) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorException, context.SourceFile); errorInfo.Message = string.Format("Error loading settings from {0}, {1} at line {2}", dataSource, xe.Message, xe.LineNumber); context.Errors.Add(errorInfo); exitCode = 1; break; } catch (Exception e) { ErrorInfo errorInfo = new ErrorInfo(ErrorCode.ErrorException, context.SourceFile); errorInfo.Message = string.Format("Error loading settings from {0}, {1}", dataSource, e.Message); context.Errors.Add(errorInfo); exitCode = 1; break; } } } } } return(exitCode); }
/// <summary> /// Replace any macros with their property value. /// </summary> /// <param name="content">Content in which to replace all macros.</param> /// <param name="context">The preprocessing context.</param> /// <returns>The content with macros replaced with corresponding property values.</returns> private string ResolveProperties(string content, PreprocessingContext context) { // search for the #foreach(property1,property2) construct which // expands the content multiple times Match match = regexForEach.Match(content); if (match.Success) { Group nameGroup = match.Groups["name"]; if (null != nameGroup && !string.IsNullOrEmpty(nameGroup.Value)) { // Remove the #foreach(...) construct Group foreachEndGroup = match.Groups["foreachend"]; content = content.Substring(0, match.Groups["foreachstart"].Index) + content.Substring(foreachEndGroup.Index + foreachEndGroup.Length); string combinedContent = ""; // if the foreach contains a prefix and wildcard, // (For example "SomePrefix*") handle it differently if (!string.IsNullOrWhiteSpace(nameGroup.Value) && nameGroup.Value.Length > 0 && nameGroup.Value.IndexOf(',') < 0 && nameGroup.Value.EndsWith("*")) { string prefix = nameGroup.Value.Substring(0, nameGroup.Value.Length - 1); foreach (PreprocessingProperty property in context.Properties) { if (property.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // values to override when resolving properties within repeated body Dictionary <string, string> overriddenValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); overriddenValues.Add("_.Key", property.Key); overriddenValues.Add("_.KeyNoPrefix", property.Key.Substring(prefix.Length)); overriddenValues.Add("_.Value", property.Value); combinedContent += ResolveProperties(content, context, overriddenValues); } } } else { // Get the values to expand int maxCount = 0; Dictionary <string, string[]> compoundValues = ExtractForEachProperties(nameGroup.Value, context, out maxCount); // Loop through the largest collection for (int i = 0; i < maxCount; i++) { // values to override when resolving properties within repeated body Dictionary <string, string> overriddenValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); // override all looped properties foreach (string key in compoundValues.Keys) { string overriddenValue = ""; string[] values = compoundValues[key]; if (values.Length > i) { overriddenValue = values[i]; } overriddenValues.Add(key, overriddenValue.Trim()); } combinedContent += ResolveProperties(content, context, overriddenValues); } } return(combinedContent); } } return(ResolveProperties(content, context, null)); }
/// <summary> /// Creates the preprocessing context. /// </summary> /// <returns>A new preprocessing context</returns> public PreprocessingContext CreatePreprocessingContext() { // Create the preprocessing context PreprocessingContext context = new PreprocessingContext(fixFalse); context.SettingsFiles = settings; context.PreserveMarkup = !clean; context.QuietMode = quiet; if (validate) { context.ValidateSettingsExist = true; context.ValidateXmlWellFormed = true; } else { context.ValidateSettingsExist = validateSettingsExist; context.ValidateXmlWellFormed = validateXmlWellFormed; } // create data sources collection List <DataSource> dataSources = new List <DataSource>(); foreach (string spreadsheetFile in spreadsheet) { DataSourceSpreadsheetFormat spreadsheetFormat = DetermineSpreadsheetFormat(spreadsheetFile); dataSources.Add(new DataSource(spreadsheetFile, DataSourceType.Spreadsheet, spreadsheetFormat)); } foreach (string databaseConnectionString in database) { dataSources.Add(new DataSource(databaseConnectionString, DataSourceType.Database)); } foreach (string customCommandLineString in custom) { dataSources.Add(new DataSource(customCommandLineString, DataSourceType.Custom)); } context.DataSources = dataSources; context.EnvironmentName = environment; context.FirstValueRowIndex = firstValueRow; context.EnvironmentNameRowIndex = environmentRow; context.DefaultValueColumnIndex = defaultValueCol; context.SettingNameColumnIndex = settingNameCol; context.List = list; context.PropertyToExtract = property; context.Delimiters = delimiters; context.NoDirectives = noDirectives; context.CountUsage = !string.IsNullOrEmpty(countReportFile); if (!string.IsNullOrEmpty(tokenStart)) { context.TokenStart = tokenStart; if (string.IsNullOrEmpty(tokenEnd)) { context.TokenEnd = tokenStart; } } if (!string.IsNullOrEmpty(tokenEnd)) { context.TokenEnd = tokenEnd; } return(context); }
/// <summary> /// Extracts for each properties. /// </summary> /// <param name="compoundValueNames">The compound value names.</param> /// <param name="context">The context.</param> /// <param name="maxCount">The max count.</param> /// <returns></returns> private Dictionary <string, string[]> ExtractForEachProperties(string compoundValueNames, PreprocessingContext context, out int maxCount) { maxCount = 0; Dictionary <string, string[]> compoundValues = new Dictionary <string, string[]>(); string[] compoundValueNamesArray = compoundValueNames.Split(','); foreach (string compoundValueName in compoundValueNamesArray) { string compoundValueNameTrimmed = compoundValueName.Trim(); if (!string.IsNullOrEmpty(compoundValueNameTrimmed)) { string compoundValue = ""; PreprocessingProperty compoundProperty = context.Properties[compoundValueNameTrimmed]; if (null != compoundProperty) { compoundValue = ResolveProperties(compoundProperty.Value, context, null); } string[] compoundValuesArray = null; if (!string.IsNullOrEmpty(compoundValue)) { compoundValuesArray = compoundValue.Split(';'); if (compoundValuesArray.Length > maxCount) { maxCount = compoundValuesArray.Length; } } compoundValues.Add(compoundValueNameTrimmed, compoundValuesArray); } } return(compoundValues); }