/// <summary> /// /// </summary> /// <param name="strs"></param> /// <param name="catalogue"></param> /// <param name="section"></param> /// <param name="iteration">Indicates if looping through Catalogues and we are not at the last element in the collection yet.</param> /// <returns></returns> private string DoReplacements(string[] strs, Catalogue catalogue, CatalogueSection section, ElementIteration iteration) { StringBuilder sb = new StringBuilder(); for (var index = 0; index < strs.Length; index++) { var str = strs[index]; string copy = str; if (str.Trim().Equals(LoopCatalogueItems, StringComparison.CurrentCultureIgnoreCase)) { index = DoReplacements(strs, index, out copy, catalogue.CatalogueItems, section); } else { foreach (var r in Replacements) { if (copy.Contains(r.Key)) { copy = copy.Replace(r.Key, ValueToString(r.Value(catalogue))); } } // when iterating we need to respect iteration symbols (e.g. $Comma). if (iteration == ElementIteration.NotIterating) { ThrowIfContainsIterationElements(copy); } else { copy = copy.Replace(Comma, iteration == ElementIteration.RegularElement ? CommaSubstitution : ""); } } sb.AppendLine(copy.TrimEnd()); } return(sb.ToString().TrimEnd()); }
/// <summary> /// Consumes from $foreach to $end looping <paramref name="catalogueItems"/> to produce output rows of string data /// </summary> /// <param name="strs">The original input in its entirety</param> /// <param name="index">The line in <paramref name="strs"/> in which the foreach was detected</param> /// <param name="result">The results of consuming the foreach block</param> /// <param name="catalogueItems"></param> /// <param name="section"></param> /// <returns>The index in <paramref name="strs"/> where the $end was detected</returns> private int DoReplacements(string[] strs, int index, out string result, CatalogueItem[] catalogueItems, CatalogueSection section) { // The foreach template block as extracted from strs StringBuilder block = new StringBuilder(); //the result of printing out the block once for each CatalogueItem item (with replacements) StringBuilder sbResult = new StringBuilder(); int i = index + 1; bool blockTerminated = false; int sectionOffset = section?.LineNumber ?? 0; //starting on the next line after $foreach until the end of the file for (; i < strs.Length; i++) { var current = strs[i]; int lineNumberHuman = i + 1 + sectionOffset; if (current.Trim().Equals(EndLoop, StringComparison.CurrentCultureIgnoreCase)) { blockTerminated = true; break; } if (current == LoopCatalogueItems) { throw new CustomMetadataReportException($"Error, encountered '{current}' on line {lineNumberHuman} before the end of current block which started on line {lineNumberHuman}. Make sure to add {EndLoop} at the end of each loop", lineNumberHuman); } block.AppendLine(current); } if (!blockTerminated) { throw new CustomMetadataReportException($"Expected {EndLoop} to match $foreach which started on line {index+1+sectionOffset}", index + 1 + sectionOffset); } for (int j = 0; j < catalogueItems.Length; j++) { sbResult.AppendLine(DoReplacements(block.ToString(), catalogueItems[j], j < catalogueItems.Length - 1 ? ElementIteration.RegularElement : ElementIteration.LastElement)); } result = sbResult.ToString(); return(i); }
private IEnumerable <CatalogueSection> SplitCatalogueLoops(string[] templateBody) { if (templateBody.Length == 0) { yield break; } CatalogueSection currentSection = null; int depth = 0; for (int i = 0; i < templateBody.Length; i++) { var str = templateBody[i]; // is it trying to loop catalogue items if (str.Trim().Equals(LoopCatalogueItems)) { if (currentSection == null || currentSection.IsPlainText) { throw new CustomMetadataReportException($"Error, Unexpected '{str}' on line {i+1}. Current section is plain text, '{LoopCatalogueItems}' can only appear within a '{LoopCatalogues}' block (you cannot mix and match top level loop elements)", i + 1); } // ignore dives into CatalogueItems depth++; // but preserve it in the body because it will be needed later currentSection.Body.Add(str); } else // is it a loop Catalogues if (str.Trim().Equals(LoopCatalogues)) { if (currentSection != null) { if (currentSection.IsPlainText) { yield return(currentSection); } else { throw new CustomMetadataReportException($"Unexpected '{str}' before the end of the last one on line {i+1}", i + 1); } } // start new section looping Catalogues currentSection = new CatalogueSection(false, i); depth = 1; } else // is it an end loop if (str.Trim().Equals(EndLoop)) { if (currentSection == null || currentSection.IsPlainText) { throw new CustomMetadataReportException($"Error, encountered '{str}' on line {i+1} while not in a {LoopCatalogues} block", i + 1); } depth--; // does end loop correspond to ending a $foreach Catalogue if (depth == 0) { yield return(currentSection); currentSection = null; } else if (depth < 0) { throw new CustomMetadataReportException($"Error, unexpected '{str}' on line {i+1}", i + 1); } else { // $end is for a CatalogueItem block so preserve it in the body currentSection.Body.Add(str); } } else { // it's just a regular line of text //if it's the first line of a new block we get a plaintext block if (currentSection == null) { currentSection = new CatalogueSection(true, i); } currentSection.Body.Add(str); } } if (currentSection != null) { if (currentSection.IsPlainText) { yield return(currentSection); } else { throw new CustomMetadataReportException($"Reached end of template without finding an expected {EndLoop}", templateBody.Length); } } }