42 private ConverterSettings _converterSettings =
null;
47 internal ConverterSettings ConverterSettings
51 return _converterSettings;
56 _converterSettings = value;
59 private System.Threading.Timer _idleTimer;
60 private readonly
object _timerLock =
new object();
61 private const int IdleTimeoutMs = 5000;
63 internal GPALConverter()
65 ConverterSettings =
new ConverterSettings();
68 private void ResetIdleTimer()
72 if (_idleTimer ==
null)
73 _idleTimer =
new System.Threading.Timer(_ => { lock (_timerLock) { ConverterSettings.ClearDerived(); } },
null, IdleTimeoutMs, System.Threading.Timeout.Infinite);
75 _idleTimer.Change(IdleTimeoutMs, System.Threading.Timeout.Infinite);
79 private void CancelIdleTimer()
83 _idleTimer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
87 #region Fluent Interface
104 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
105 ConverterSettings.ClearDerived();
106 ConverterSettings.InputClass =
null;
107 ConverterSettings.InputClassType =
null;
108 ConverterSettings.InputClassElementType =
null;
109 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
110 ConverterSettings.ColumnNames =
new GPALGrid<string>();
111 ConverterSettings.InputDictionary = inputDictionary;
112 ConverterSettings.InDataFormat.Add(DataFormat.DICTIONARY);
124 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
125 ConverterSettings.ClearDerived();
126 ConverterSettings.InputClass =
null;
127 ConverterSettings.InputClassType =
null;
128 ConverterSettings.InputClassElementType =
null;
129 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
130 ConverterSettings.ColumnNames =
new GPALGrid<string>();
131 ConverterSettings.InputDatabase = inputDatabase;
132 ConverterSettings.InDataFormat.Add(DataFormat.DATABASE);
143 ConverterSettings.OutputFile =
null;
144 ConverterSettings.OutputClass =
null;
145 ConverterSettings.OutputClassType =
null;
146 ConverterSettings.OutputClassElementType =
null;
147 ConverterSettings.OutputGrid =
null;
149 ConverterSettings.OutputDatabase = outputDatabase;
150 ConverterSettings.OutDataFormat = DataFormat.DATABASE;
151 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Saving [{ConverterSettings.InDataFormat.FirstOrDefault()}] data to database.",
this, GPALObjectType.Converter);
152 return Convert(ref outputDatabase);
162 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
163 ConverterSettings.ClearDerived();
164 ConverterSettings.InputClass =
null;
165 ConverterSettings.InputClassType =
null;
166 ConverterSettings.InputClassElementType =
null;
167 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
168 ConverterSettings.ColumnNames =
new GPALGrid<string>();
172 ConverterSettings.InputFile =
null;
173 ConverterSettings.InputDatabase =
null;
174 ConverterSettings.InputGrid.Add(inputGrid);
175 ConverterSettings.InDataFormat.Add(DataFormat.GRID);
176 ConverterSettings.InDelimiter =
',';
187 ConverterSettings.OutputDatabase =
null;
188 ConverterSettings.OutputFile =
null;
189 ConverterSettings.OutputClassType = outputGrid.GetType();
190 ConverterSettings.OutputClassElementType = ConverterSettings.OutputClassType.GetElementType();
191 if (
null == ConverterSettings.OutputClassElementType && 0 < ConverterSettings.OutputClassType.GetGenericArguments().Length)
192 if (
true ==
ConverterHelper.IsDictionaryType(ConverterSettings.OutputClassType))
193 ConverterSettings.OutputClassElementType = ConverterSettings.OutputClassType.GetGenericArguments()[1];
195 ConverterSettings.OutputClassElementType = ConverterSettings.OutputClassType.GetGenericArguments()[0];
196 ConverterSettings.OutputGrid = outputGrid;
197 ConverterSettings.OutDataFormat = DataFormat.GRID;
198 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Saving [{ConverterSettings.InDataFormat.FirstOrDefault()}] data to grid.",
this, GPALObjectType.Converter);
200 Convert(ref outputGrid);
222 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
223 ConverterSettings.ClearDerived();
224 ConverterSettings.InputClass =
null;
225 ConverterSettings.InputClassType =
null;
226 ConverterSettings.InputClassElementType =
null;
227 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
228 ConverterSettings.ColumnNames =
new GPALGrid<string>();
229 ConverterSettings.InputData.Add(inputData);
230 if (
null != ConverterSettings.InDelimiter &&
true == ConverterSettings.InDelimiter.HasValue)
231 ConverterSettings.InDataFormat.Add(GetDataFormat(ConverterSettings.InDelimiter.Value));
233 ConverterSettings.InDataFormat.Add(DataFormatDetector.DetectFormat(inputData));
246 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
247 ConverterSettings.ClearDerived();
248 ConverterSettings.InputClass =
null;
249 ConverterSettings.InputClassType =
null;
250 ConverterSettings.InputClassElementType =
null;
251 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
252 ConverterSettings.ColumnNames =
new GPALGrid<string>();
253 ConverterSettings.InputFile = inputFile;
254 foreach (
string filename
in inputFile.Filenames)
255 ConverterSettings.InDataFormat.Add(
ConverterHelper.GetDataFormatFromExtension(filename));
265 ConverterSettings.OutputFile = outputFile;
267 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Appending [{ConverterSettings.InDataFormat.FirstOrDefault()}] data as [{ConverterSettings.OutDataFormat}] to [{outputFile.Filename}].",
this, GPALObjectType.Converter);
268 return Convert(ref outputFile,
true);
284 ConverterSettings.OutputFile = outputFile;
286 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Saving [{ConverterSettings.InDataFormat.FirstOrDefault()}] data as [{ConverterSettings.OutDataFormat}] to [{outputFile.Filename}].",
this, GPALObjectType.Converter);
287 Convert(ref outputFile);
302 if (inputClass is IGPALGrid<string> handedOverGrid)
306 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
307 ConverterSettings.ClearDerived();
308 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
309 ConverterSettings.ColumnNames =
new GPALGrid<string>();
310 ConverterSettings.InputFile =
null;
311 ConverterSettings.InputClass = inputClass;
312 ConverterSettings.InputClassType = inputClass.GetType();
313 ConverterSettings.InputClassElementType = ConverterSettings.InputClassType.GetElementType();
314 if (
null == ConverterSettings.InputClassElementType && ConverterSettings.InputClassType.IsGenericType)
316 var genericArgs = ConverterSettings.InputClassType.GetGenericArguments();
317 ConverterSettings.InputClassElementType =
ConverterHelper.IsDictionaryType(ConverterSettings.InputClassType) && genericArgs.Length > 1
319 : genericArgs.FirstOrDefault();
323 ConverterHelper.ConverterSettings = ConverterSettings;
324 ConverterSettings.InputDictionary =
new List<Dictionary<object, dynamic>> {
ConverterHelper.ConvertClassToDictionary(inputClass,
null != ConverterSettings.InputClassElementType ? ConverterSettings.InputClassElementType.ToString() : ConverterSettings.InputClassType.ToString(), visited) };
325 ConverterSettings.InputData.Add(
ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out IGPALGrid<string> outGrid));
326 ConverterSettings.InDataFormat.Add(DataFormat.CLASS);
327 ConverterSettings.InputGrid.Add(outGrid);
333 ConverterSettings.OutputFile =
null;
334 ConverterSettings.OutputClassType = outputClass.GetType();
335 ConverterSettings.OutputClassElementType =
ConverterSettings.OutputClassType.GetElementType();
341 if (genericArgs.Length > 0)
346 ConverterSettings.OutputClassElementType = genericArgs[1];
349 else if (ConverterSettings.OutputClassType.IsGenericType &&
350 ConverterSettings.OutputClassType.GetGenericTypeDefinition() == typeof(IGPALGrid<>))
353 var elementT = genericArgs[0];
354 ConverterSettings.OutputClassElementType = typeof(List<>).MakeGenericType(elementT);
359 ConverterSettings.OutputClassElementType = genericArgs[0];
364 ConverterSettings.OutDataFormat = DataFormat.CLASS;
365 Convert(ref outputClass);
381 if (0 == ConverterSettings.InDataFormat.Count)
382 ConverterSettings.InDataFormat.Add(inDataFormat);
384 ConverterSettings.InDataFormat[ConverterSettings.InDataFormat.Count - 1] = inDataFormat;
395 ConverterSettings.OutDataFormat = outDataFormat;
405 ConverterSettings.FirstLineIsColumnHeaders = firstLineHeaders;
406 ConverterSettings.InputFirstLineIsColumnHeaders = firstLineHeaders;
416 ConverterSettings.IgnoreFirstLineColumnHeaders = ignoreFirstLine;
426 ConverterSettings.FieldsEnclosedInQuotes = fieldsEnclosedInQuotes;
440 ConverterSettings.ColumnNames =
new GPALGrid<string>();
441 if (
null != columnNames && 0 < columnNames.Count)
442 ConverterSettings.ColumnNames.AddRow(
new List<string>(columnNames));
454 if (0 == ConverterSettings.ColumnNames.Count())
455 ConverterSettings.ColumnNames.AddRow(
new List<string>());
456 ConverterSettings.ColumnNames[0].Add(columnName);
467 private List<string> ResolveEffectiveColumns(List<string> sourceColumns)
469 List<string> caller = (
null != ConverterSettings.ColumnNames && 0 <
ConverterSettings.ColumnNames.Count())
472 if (
null == caller || 0 == caller.Count)
473 return (
null != sourceColumns) ?
new List<string>(sourceColumns) : new List<string>();
475 List<string> effective =
new List<string>(caller);
476 if (
null != sourceColumns)
477 for (
int i = effective.Count; i < sourceColumns.Count; i++)
478 effective.Add(sourceColumns[i]);
492 ConverterSettings.OutputFile =
null;
493 ConverterSettings.OutputClass =
null;
494 ConverterSettings.OutputClassType =
null;
495 ConverterSettings.OutputClassElementType =
null;
496 ConverterSettings.OutputGrid =
null;
499 if (ConverterSettings.InDataFormat.FirstOrDefault() != DataFormat.NOTSET)
500 ConverterSettings.OutDataFormat = ConverterSettings.InDataFormat.FirstOrDefault();
502 outputData = FormatPretty(ConverterSettings.InputData.FirstOrDefault());
503 ConverterSettings.OutputData = outputData;
508 #endregion Fluent Interface
528 bool DropAliasesFor(
object payload)
530 int limit =
GPAL.GPALSettings.YamlAliasLimit;
537 retVal =
false == cyclic && nodes >= limit;
539 if (
true == cyclic && nodes >= limit)
540 GPAL.
PublishSimpleEvent(Enums.GPALEventType.DEBUG, $
"The payload holds a cycle, so its aliases are kept whatever its size. Without them YAML cannot write it at all",
null, Enums.GPALObjectType.Converter);
541 else if (
true == retVal)
542 GPAL.
PublishSimpleEvent(Enums.GPALEventType.DEBUG, $
"The payload reached the alias limit of [{limit}] nodes and holds no cycle, so a shared reference is written out again rather than as an alias. GPAL.WithYamlAliasLimit moves the limit",
null, Enums.GPALObjectType.Converter);
558 int InputWithin(
int inputIdx,
int inputCount,
string whatIsMissing)
560 int within = inputIdx;
562 if (inputCount <= inputIdx)
564 within = inputCount - 1;
565 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Output [{inputIdx + 1}] has no {whatIsMissing} of its own, only [{inputCount}] given. Using the last one.",
this, GPALObjectType.Converter);
571 internal IAllowConverterInputAndActions Convert<T>(ref T outputClass,
bool appendToFile =
false)
574 ConverterHelper.ConverterSettings = ConverterSettings;
577 IGPALGrid<string> outGrid = GPAL.Grid.ToGPALObject();
578 ConverterSettings.OutputData =
null;
583 if (
null != ConverterSettings.InputFile)
585 if (
true == File.Exists(ConverterSettings.InputFile))
587 if (
false == FileHelper.TokenizeFile(ConverterSettings.InputFile, ConverterSettings))
589 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to tokenize file [{ConverterSettings.InputFile.Filename}]. Continuing.", ConverterSettings, GPALObjectType.Other);
592 ConverterSettings.InputDictionary = ((IGPALFileInternal)ConverterSettings.InputFile).FileDictionary;
593 ConverterSettings.InputData = ((IGPALFileInternal)ConverterSettings.InputFile).FileData;
594 ConverterSettings.InputGrid = ((IGPALFileInternal)ConverterSettings.InputFile).FileGrid;
597 if (ConverterHelper.IsDelimitedDataFormat(ConverterHelper.GetDataFormatFromExtension(ConverterSettings.InputFile.Filename))
598 && 0 == ConverterSettings.ColumnNames.Count())
600 ConverterSettings.ColumnNames =
new GPALGrid<string>();
601 foreach (List<string> colRow
in ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ColumnList)
602 ConverterSettings.ColumnNames.AddRow(
new List<string>(colRow));
604 ConverterSettings.ExcelRowsPerSheet = ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ExcelRowsPerSheet;
605 ConverterSettings.ExcelSheetNames = ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ExcelSheetNames;
606 ConverterSettings.FirstLineIsColumnHeaders = (bool)(0 < ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.FirstLineIsColumnNames.Count() ? ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.FirstLineIsColumnNames?[0] : ConverterSettings.InputFirstLineIsColumnHeaders);
607 ConverterSettings.InDelimiter = ((IGPALFileInternal)ConverterSettings.InputFile).Delimiter[0];
614 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"File missing: [{ConverterSettings.InputFile.Filename}]", ConverterSettings, GPALObjectType.Other);
616 else if (0 < ConverterSettings.InputData.Count)
618 for (
int idx = 0; idx < ConverterSettings.InputData.Count; idx++)
620 switch (ConverterSettings.InDataFormat[idx])
622 case DataFormat.CUSTOM_DELIMITER:
623 case DataFormat.CARET:
626 case DataFormat.PIPE:
629 ConverterSettings.InputDictionary.Clear();
630 ConverterSettings.InputDictionary.Add(ConverterHelper.ConvertDelimitedToDictionary(ConverterSettings,
new StringReader(ConverterSettings.InputData[idx])));
631 ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid);
632 ConverterSettings.InputGrid.Add(outGrid);
634 case DataFormat.STRING:
635 ConverterSettings.InputGrid.Add(GPAL.Grid.AddRow(
new List<string> { ConverterSettings.InputData[idx] }).ToGPALObject());
636 ConverterSettings.InputDictionary.Clear();
637 ConverterSettings.InputDictionary.Add(ConverterHelper.ConvertDelimitedToDictionary(ConverterSettings,
new StringReader(ConverterSettings.InputData[idx])));
639 case DataFormat.DICTIONARY:
641 case DataFormat.JSON:
642 var desObj = JsonConvert.DeserializeObject<dynamic>(ConverterSettings.InputData[idx],
new JsonConverter[] {
new CustomJsonConverter() });
645 Dictionary<object, dynamic> jsonDict;
646 if (desObj is IDictionary)
648 jsonDict = (Dictionary<object, dynamic>)desObj;
650 else if (desObj is IList jsonList)
654 jsonDict =
new Dictionary<object, dynamic>();
656 foreach (var item
in jsonList)
658 jsonDict[$
"GPALKEY{rootIdx++:D4}"] = item;
664 jsonDict =
new Dictionary<object, dynamic> { [
"GPALKEY0000"] = desObj };
667 ConverterSettings.InputDictionary.Add(jsonDict);
668 ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid);
669 ConverterSettings.InputGrid.Add(outGrid);
678 case DataFormat.XLSX:
681 ConverterSettings.InputXmlDocument.PreserveWhitespace =
true;
682 ConverterSettings.InputXmlDocument.Load(ConverterSettings.InputData[idx]);
683 ConverterSettings.InputDictionary.Add(ConverterHelper.ConvertXMLToDictionary(ConverterSettings.InputXmlDocument));
684 ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid);
685 ConverterSettings.InputGrid.Add(outGrid);
687 case DataFormat.YAML:
688 var deserializer =
new DeserializerBuilder()
689 .WithNamingConvention(CamelCaseNamingConvention.Instance)
692 dynamic yamlObject = deserializer.Deserialize<
object>(ConverterSettings.InputData[idx]);
693 var yamlGridRows =
new List<List<string>>();
694 var processedYamlDict =
new Dictionary<dynamic, dynamic>();
696 if (yamlObject is IDictionary yamlMapping)
698 foreach (var key
in yamlMapping.Keys)
700 string keyStr = key?.ToString() ??
"null_key";
701 processedYamlDict[keyStr] = FileHelper.ProcessDictionaryValue(yamlMapping[key], yamlGridRows);
704 else if (yamlObject is IList yamlSequence)
709 foreach (var item
in yamlSequence)
711 processedYamlDict[$
"GPALKEY{rootIdx++:D4}"] = FileHelper.ProcessDictionaryValue(item, yamlGridRows);
717 processedYamlDict[
"GPALKEY0000"] = FileHelper.ProcessDictionaryValue(yamlObject, yamlGridRows);
720 var yamlGridRow =
new GPALGrid<string>();
721 foreach (var row
in yamlGridRows)
723 yamlGridRow.AddRow(row);
726 ConverterSettings.InputDictionary.Add(processedYamlDict);
727 ConverterSettings.InputGrid.Add(yamlGridRow);
729 case DataFormat.HTML:
730 ConverterSettings.InputDictionary = ConverterHelper.ConvertHtmlToDictionary(ConverterSettings.InputData[idx]);
731 ConverterSettings.InputData.Add(ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid));
732 ConverterSettings.InputGrid.Add(outGrid);
737 else if (
null != ConverterSettings.InputDatabase)
739 List<string> dbColumns =
new List<string>();
742 DatabaseHelper.TokenizeDatabase(
null, ConverterSettings.InputDatabase);
743 ConverterSettings.InputGrid.Add(ConverterSettings.InputDatabase.Tokens);
744 if (0 < ((GPALDatabase)ConverterSettings.InputDatabase).DatabaseSettings.ColumnList.Count)
745 foreach (DatabaseSettings.DatabaseColumn column in ((GPALDatabase)ConverterSettings.InputDatabase).DatabaseSettings.ColumnList)
746 dbColumns.Add(column.ColumnName);
750 List<string> columns = ResolveEffectiveColumns(dbColumns);
751 if (0 == ConverterSettings.ColumnNames.Count() && 0 < columns.Count)
752 ConverterSettings.ColumnNames.AddRow(
new List<string>(columns));
754 ConverterSettings.InputDictionary = ConverterHelper.ConvertGridToDictionary(columns, ConverterSettings.InputDatabase.Tokens);
756 else if (
null != ConverterSettings.InputGrid && 0 < ConverterSettings.InputGrid.Count)
761 List<string> columns = ResolveEffectiveColumns(
null);
762 bool haveColumns = 0 < columns.Count;
764 ConverterSettings.InputDictionary.Clear();
765 foreach (IGPALGrid<string> inGrid
in ConverterSettings.InputGrid)
769 Dictionary<object, dynamic> source =
new Dictionary<object, dynamic>();
771 foreach (List<string> gridRow
in inGrid)
775 Dictionary<string, string> rowDict =
new Dictionary<string, string>();
776 for (
int i = 0; i < gridRow.Count; i++)
777 rowDict[i < columns.Count ? columns[i] : $
"Column{i + 1}"] = gridRow[i];
778 source[$
"GPALKEY{lineNumber:D4}"] = rowDict;
781 source[$
"GPALKEY{lineNumber:D4}"] =
new List<string>(gridRow);
784 ConverterSettings.InputDictionary.Add(source);
787 else if (
null != ConverterSettings.InputClass && 0 < ConverterSettings.InDataFormat.Count)
790 HashSet<object> visited =
new HashSet<object>(ConverterHelper.ReferenceEqualityComparer.Instance);
791 IGPALGrid<string> repopGrid;
792 ConverterSettings.InputDictionary =
new List<Dictionary<object, dynamic>> {
793 ConverterHelper.ConvertClassToDictionary(
794 ConverterSettings.InputClass,
795 null != ConverterSettings.InputClassElementType
796 ? ConverterSettings.InputClassElementType.ToString()
797 : ConverterSettings.InputClassType.ToString(),
800 ConverterSettings.InputData.Add(ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out repopGrid));
801 ConverterSettings.InDataFormat.Add(DataFormat.CLASS);
802 ConverterSettings.InputGrid.Add(repopGrid);
806 StringBuilder stringBuilder =
new StringBuilder();
807 if (
null != ConverterSettings.OutputFile)
813 int inputFileCount = Math.Max(1, ConverterSettings.InDataFormat.Count);
814 GPALFileSettings outputFileSettings = ((IGPALFileInternal)ConverterSettings.OutputFile).FileSettings;
819 List<string> outputPaths =
new List<string>(outputFileSettings.Filenames);
821 if (0 < outputPaths.Count && outputPaths.Count < inputFileCount)
823 string pattern = outputPaths[outputPaths.Count - 1];
824 string patternDirectory = Path.GetDirectoryName(pattern);
825 string patternName = Path.GetFileNameWithoutExtension(pattern);
826 string patternExtension = Path.GetExtension(pattern);
828 for (
int newFileIdx = outputPaths.Count; newFileIdx < inputFileCount; newFileIdx++)
830 string generatedFilename = Path.Combine(patternDirectory, $
"{patternName}_{newFileIdx + 1}{patternExtension}");
831 outputPaths.Add(generatedFilename);
832 ConverterSettings.OutputFile.ReturnFilenames.Add(generatedFilename);
837 DataFormat savedOutDataFormat = ConverterSettings.OutDataFormat;
838 foreach (
string filename
in outputPaths)
840 int inputIdx = Math.Min(fileIdx, inputFileCount - 1);
843 if (
null != ConverterSettings.InputFile && inputIdx < ((IGPALFileInternal)ConverterSettings.InputFile).Delimiter.Count)
844 ConverterSettings.InDelimiter = ((IGPALFileInternal)ConverterSettings.InputFile).Delimiter[inputIdx];
847 if (DataFormat.NOTSET == savedOutDataFormat)
848 ConverterSettings.OutDataFormat = ConverterHelper.GetDataFormatFromExtension(filename);
855 if (fileIdx < ((IGPALFileInternal)ConverterSettings.OutputFile).FirstLineIsColumnNames.Count)
858 ConverterSettings.FirstLineIsColumnHeaders = ((IGPALFileInternal)ConverterSettings.OutputFile).FirstLineIsColumnNames[fileIdx];
863 ConverterSettings.FirstLineIsColumnHeaders =
864 (
null != ConverterSettings.ColumnNames && 0 < ConverterSettings.ColumnNames.Count())
865 || ConverterSettings.InputFirstLineIsColumnHeaders;
868 if (
true == appendToFile)
869 ConverterSettings.FirstLineIsColumnHeaders =
false;
871 if (fileIdx < ((IGPALFileInternal)ConverterSettings.OutputFile).FieldsEnclosedInQuotes.Count)
872 ConverterSettings.FieldsEnclosedInQuotes = ((IGPALFileInternal)ConverterSettings.OutputFile).FieldsEnclosedInQuotes[fileIdx];
874 if (
null == ConverterSettings.OutDelimiter)
875 ConverterSettings.OutDelimiter = ConverterHelper.GetDelimiterFromFormat(ConverterSettings.OutDataFormat);
877 if (
null != ConverterSettings.ColumnNames && 0 == ConverterSettings.ColumnNames.Count())
879 List<string> columns =
new List<string>();
881 if ((ConverterHelper.IsDelimitedDataFormat(ConverterSettings.OutDataFormat) || DataFormat.XLSX == ConverterSettings.OutDataFormat) &&
null != ConverterSettings.OutputFile &&
null != ((IGPALFileInternal)ConverterSettings.OutputFile).FileSettings.ColumnList && fileIdx < ((IGPALFileInternal)ConverterSettings.OutputFile).FileSettings.ColumnList.Count())
882 foreach (
string columnName
in ((IGPALFileInternal)ConverterSettings.OutputFile).FileSettings.ColumnList[fileIdx])
883 columns.Add(columnName);
884 else if (inputIdx < ConverterSettings.InDataFormat.Count && (ConverterHelper.IsDelimitedDataFormat(ConverterSettings.InDataFormat[inputIdx]) || DataFormat.XLSX == ConverterSettings.InDataFormat[inputIdx]) &&
null != ConverterSettings.InputFile &&
null != ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ColumnList && inputIdx < ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ColumnList.Count())
885 foreach (
string columnName
in ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ColumnList[inputIdx])
886 columns.Add(columnName);
888 if (0 < columns.Count)
889 ConverterSettings.ColumnNames.AddRow(columns);
892 switch (ConverterSettings.OutDataFormat)
894 case DataFormat.CUSTOM_DELIMITER:
895 case DataFormat.CARET:
898 case DataFormat.PIPE:
901 case DataFormat.NOTSET:
902 if (0 < ConverterSettings.InDataFormat.Count)
904 switch (ConverterSettings.InDataFormat[inputIdx])
906 case DataFormat.CARET:
907 case DataFormat.COLON:
909 case DataFormat.CUSTOM_DELIMITER:
912 case DataFormat.HYPHEN:
914 case DataFormat.PIPE:
915 case DataFormat.SEMICOLON:
916 case DataFormat.SPACE:
919 if (inputIdx < ConverterSettings.InputData.Count)
920 ConverterSettings.OutputData = ConverterSettings.InputData[inputIdx];
922 ConverterSettings.OutputData = File.ReadAllText(ConverterSettings.InputFile.Filenames[inputIdx]);
923 if (
null != ConverterSettings.OutputData &&
null != ConverterSettings.InDelimiter &&
true == ConverterSettings.InDelimiter.HasValue)
925 using (StringReader reader =
new StringReader(ConverterSettings.OutputData))
928 StringBuilder lineBuilder =
new StringBuilder();
929 while ((line = reader.ReadLine()) !=
null)
931 string[] tokens = line.Split(ConverterSettings.InDelimiter.Value);
932 foreach (
string str
in tokens)
933 stringBuilder.Append(ConverterHelper.CreateCSVToken(str, ConverterSettings));
934 if (0 < stringBuilder.Length)
935 stringBuilder.Remove(stringBuilder.Length - 1, 1);
936 lineBuilder.AppendLine(stringBuilder.ToString());
937 stringBuilder.Clear();
939 ConverterSettings.OutputData = lineBuilder.ToString();
942 stringBuilder.Append(ConverterSettings.OutputData);
944 case DataFormat.HTML:
947 case DataFormat.CLASS:
949 stringBuilder.Append(ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid));
952 if (
true == appendToFile)
953 File.AppendAllText(filename, stringBuilder.ToString());
955 File.WriteAllText(filename, stringBuilder.ToString());
958 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"File [{ConverterSettings.InputFile}] does not exist(?) as no DATA FORMAT has been set.",
this, GPALObjectType.Converter);
960 case DataFormat.JSON:
961 if (ConverterSettings.InputDictionary.Count > 0)
963 int dictIdx = InputWithin(inputIdx, ConverterSettings.InputDictionary.Count,
"input");
964 var rootDict = ConverterSettings.InputDictionary[dictIdx] as IDictionary;
968 if (rootDict !=
null && ConverterHelper.IsArrayLike(rootDict))
970 var list =
new List<object>();
971 foreach (DictionaryEntry e
in rootDict)
973 string k = e.Key.ToString();
974 if (k.StartsWith(
"GPALKEY"))
983 rawObject = rootDict ?? ConverterSettings.InputDictionary[dictIdx];
986 dynamic cleanObject = ConverterHelper.NormalizeForCleanJson(rawObject);
987 cleanObject = ConverterHelper.StripGpalKeys(cleanObject);
989 var settings =
new JsonSerializerSettings
994 new IPAddressJsonConverter(),
995 new ComplexJsonConverter(),
996 new UriJsonConverter(),
997 new VersionJsonConverter(),
1001 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
1002 Formatting = Newtonsoft.Json.Formatting.Indented
1005 ConverterSettings.OutputData = JsonConvert.SerializeObject(cleanObject, settings);
1008 File.AppendAllText(filename, ConverterSettings.OutputData);
1010 File.WriteAllText(filename, ConverterSettings.OutputData);
1013 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1018 case DataFormat.XLSX:
1019 if (inputIdx < ConverterSettings.InputGrid.Count)
1020 ExcelHelper.WriteXlsxFromTokenizedData(
1021 outputPath: filename,
1022 grid: ConverterSettings.InputGrid[inputIdx],
1023 rowsPerSheet: ConverterSettings.ExcelRowsPerSheet[inputIdx],
1024 columnNamesPerSheet: ConverterSettings.ColumnNames,
1025 sheetNames: ConverterSettings.ExcelSheetNames[inputIdx],
1026 writeHeaders: ConverterSettings.FirstLineIsColumnHeaders
1029 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1031 case DataFormat.XML:
1032 if (0 < ConverterSettings.InputDictionary.Count)
1034 ConverterSettings.OutputXmlDocument =
new XmlDocument();
1037 var rootElement = ConverterSettings.OutputXmlDocument.CreateElement(
"root");
1038 ConverterSettings.OutputXmlDocument.AppendChild(rootElement);
1041 if (
true == appendToFile)
1043 XmlDocument xmlDocument =
new XmlDocument();
1046 xmlDocument.Load(filename);
1047 ConverterSettings.OutputXmlDocument = xmlDocument;
1048 rootElement = (XmlElement)xmlDocument.GetElementsByTagName(
"root")[0];
1050 catch (Exception ex)
1052 GPAL.PublishSimpleEvent(GPALEventType.INFO, $
"File [{filename}] does not exist to append XML to, writing new.",
this, GPALObjectType.Converter, ex);
1057 foreach (Dictionary<object, dynamic> dict
in ConverterSettings.InputDictionary)
1059 if (DataFormat.HTML == ConverterSettings.InDataFormat[dictCount])
1060 ConverterHelper.ConvertHtmlDictionaryToXml(dict, rootElement);
1061 else if (DataFormat.XML == ConverterSettings.InDataFormat[dictCount])
1062 ConverterHelper.ConvertDictionaryToXmlElement(dict, rootElement,
false);
1064 ConverterHelper.ConvertDictionaryToXmlElement(dict, rootElement);
1069 string inputFilename = ConverterSettings.InputFile?.Filename;
1071 if (
true == ConverterSettings.InputFile?.Filename.ToLower().EndsWith(
"xml"))
1073 string header = ConverterHelper.GetXmlHeaderFromFile(inputFilename);
1076 if (0 < header.Length)
1077 using (var writer =
new StreamWriter(filename, append:
false))
1079 writer.Write(header);
1081 using (var xmlWriter = XmlWriter.Create(writer,
new XmlWriterSettings
1083 OmitXmlDeclaration =
true,
1085 Encoding = writer.Encoding
1088 ConverterSettings.OutputXmlDocument.Save(xmlWriter);
1092 ConverterSettings.OutputXmlDocument.Save(filename);
1095 ConverterSettings.OutputXmlDocument.Save(filename);
1098 case DataFormat.YAML:
1099 if (ConverterSettings.InputClass !=
null)
1101 var serializerBuilder =
new SerializerBuilder()
1102 .WithObjectGraphTraversalStrategyFactory((ti, tr, tc, depth)
1103 =>
new FullObjectGraphTraversalStrategy(ti, tr, depth, CamelCaseNamingConvention.Instance,
new DefaultObjectFactory()))
1104 .WithNamingConvention(CamelCaseNamingConvention.Instance)
1105 .WithEventEmitter(next =>
new ExplicitNullYamlEventEmitter(next))
1106 .WithEventEmitter(next =>
new HumanReadableAliasEventEmitter(next))
1107 .WithTypeConverter(
new IPAddressYamlConverter())
1108 .WithTypeConverter(
new ComplexYamlConverter())
1109 .WithTypeConverter(
new UriYamlConverter())
1110 .WithTypeConverter(
new NullableBigIntegerYamlConverter())
1111 .WithTypeConverter(
new NullableGuidYamlConverter())
1112 .WithTypeConverter(
new NullableTimeSpanYamlConverter())
1113 .WithTypeConverter(
new VersionYamlConverter())
1114 .WithTypeConverter(
new DateTimeOffsetYamlConverter())
1115 .WithTypeConverter(
new QuotedStringYamlConverter())
1116 .WithTypeConverter(
new WaitTimeYamlConverter());
1118 if (
true == DropAliasesFor(ConverterSettings.InputClass))
1119 serializerBuilder = serializerBuilder.DisableAliases();
1121 var serializer = serializerBuilder.Build();
1123 ConverterSettings.OutputData = serializer.Serialize(ConverterSettings.InputClass);
1125 if (
true == appendToFile)
1126 File.AppendAllText(filename, ConverterSettings.OutputData);
1128 File.WriteAllText(filename, ConverterSettings.OutputData);
1130 else if (0 < ConverterSettings.InputDictionary.Count)
1132 int yamlIdx = InputWithin(inputIdx, ConverterSettings.InputDictionary.Count,
"input");
1133 bool largeGraph = DropAliasesFor(ConverterSettings.InputDictionary[yamlIdx]);
1135 var serializerBuilder =
new SerializerBuilder()
1136 .WithEventEmitter(next =>
new ExplicitNullYamlEventEmitter(next))
1137 .WithEventEmitter(next =>
new HumanReadableAliasEventEmitter(next))
1138 .WithNamingConvention(CamelCaseNamingConvention.Instance)
1139 .WithTypeConverter(
new IPAddressYamlConverter())
1140 .WithTypeConverter(
new ComplexYamlConverter())
1141 .WithTypeConverter(
new UriYamlConverter())
1142 .WithTypeConverter(
new NullableBigIntegerYamlConverter())
1143 .WithTypeConverter(
new NullableGuidYamlConverter())
1144 .WithTypeConverter(
new NullableTimeSpanYamlConverter())
1145 .WithTypeConverter(
new VersionYamlConverter())
1146 .WithTypeConverter(
new DateTimeOffsetYamlConverter())
1147 .WithTypeConverter(
new QuotedStringYamlConverter())
1148 .WithTypeConverter(
new WaitTimeYamlConverter());
1150 if (
true == largeGraph)
1151 serializerBuilder = serializerBuilder.DisableAliases();
1153 var serializer = serializerBuilder.Build();
1155 object CleanForYaml(
object input)
1161 if (input is IDictionary otherShapedDict &&
false == (input is Dictionary<object, dynamic>))
1163 Dictionary<object, dynamic> asGpalDict =
new Dictionary<object, dynamic>();
1165 foreach (DictionaryEntry entry
in otherShapedDict)
1166 asGpalDict[entry.Key] = entry.Value;
1171 if (input is Dictionary<object, dynamic> d)
1174 bool isXmlDerived = d.Keys.Cast<
object>().Any(k =>
1175 k.ToString() ==
"#text" || k.ToString().StartsWith(
"@"));
1177 if (ConverterHelper.IsArrayLike(d))
1180 var list =
new List<object>();
1181 foreach (var p
in d)
1183 string k = p.Key.ToString();
1185 if (k.StartsWith(
"GPALKEY"))
1187 list.Add(CleanForYaml(p.Value));
1195 var cleanDict =
new Dictionary<object, dynamic>();
1197 string textValue =
null;
1198 if (d.TryGetValue(
"#text", out var txt) && isXmlDerived)
1200 textValue = txt?.ToString();
1204 foreach (var p
in d)
1206 string k = p.Key.ToString();
1208 k = k.Replace(
@"GPAL_",
"");
1210 if (k.StartsWith(
"@") && isXmlDerived)
1212 cleanDict[k.Substring(1)] = p.Value;
1217 foreach (var p
in d)
1219 string k = p.Key.ToString();
1221 k = k.Replace(
@"GPAL_",
"");
1223 if (k ==
"#text" || k.StartsWith(
"@") || k.StartsWith(
"GPALKEY"))
1226 cleanDict[k] = CleanForYaml(p.Value);
1230 if (isXmlDerived && textValue !=
null)
1232 if (cleanDict.Count == 0)
1235 if (
double.TryParse(textValue, out
double num))
1242 cleanDict[
"value"] = textValue;
1249 else if (input is IEnumerable enumerable && !(input is
string))
1251 var list =
new List<object>();
1252 foreach (var item
in enumerable)
1254 list.Add(CleanForYaml(item));
1262 Dictionary<object, dynamic> rootValue = ConverterSettings.InputDictionary[yamlIdx];
1263 dynamic cleanRoot = CleanForYaml(rootValue);
1264 cleanRoot = ConverterHelper.StripGpalKeys(cleanRoot);
1266 if (
true == ConverterHelper.IsEnumerableObject(cleanRoot) && !(cleanRoot is IDictionary) && !(cleanRoot is
string))
1268 cleanRoot =
new Dictionary<object, object>
1270 [
"values"] = cleanRoot
1274 ConverterSettings.OutputData = serializer.Serialize(cleanRoot);
1276 if (
true == appendToFile)
1277 File.AppendAllText(filename, ConverterSettings.OutputData);
1279 File.WriteAllText(filename, ConverterSettings.OutputData);
1282 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1284 case DataFormat.HTML:
1285 if (0 < ConverterSettings.InputDictionary.Count)
1287 switch (ConverterSettings.InDataFormat[inputIdx])
1289 case DataFormat.CARET:
1290 case DataFormat.COLON:
1291 case DataFormat.CSV:
1292 case DataFormat.CUSTOM_DELIMITER:
1293 case DataFormat.DOT:
1294 case DataFormat.HYPHEN:
1295 case DataFormat.LOG:
1296 case DataFormat.PIPE:
1297 case DataFormat.SEMICOLON:
1298 case DataFormat.SPACE:
1299 case DataFormat.TAB:
1300 case DataFormat.PRN:
1302 var sb =
new StringBuilder();
1304 var grid = ConverterSettings.InputGrid;
1305 if (grid ==
null || grid.Count == 0)
1307 ConverterSettings.OutputData =
"<p>No data</p>";
1311 int gridIdx = InputWithin(inputIdx, grid.Count,
"input grid");
1313 sb.AppendLine(
"<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse: collapse; font-family: Arial, sans-serif; width: 100%;\">");
1316 List<string> headers =
null;
1317 int dataStartRow = 0;
1319 if (ConverterSettings.ColumnNames?.Count() > fileIdx && ConverterSettings.ColumnNames[fileIdx].Count > 0)
1321 headers = ConverterSettings.ColumnNames[fileIdx];
1324 else if (ConverterSettings.FirstLineIsColumnHeaders && grid.Count > 0)
1326 headers = grid[gridIdx][0];
1331 if (headers !=
null)
1333 sb.AppendLine(
" <thead>");
1334 sb.AppendLine(
" <tr>");
1335 foreach (var h
in headers)
1337 string safeHeader = System.Net.WebUtility.HtmlEncode(h ??
"");
1338 sb.AppendLine($
" <th>{safeHeader}</th>");
1340 sb.AppendLine(
" </tr>");
1341 sb.AppendLine(
" </thead>");
1345 sb.AppendLine(
" <tbody>");
1347 for (
int rowIdx = dataStartRow; rowIdx < grid[gridIdx].Count(); rowIdx++)
1349 var row = grid[gridIdx][rowIdx];
1350 sb.AppendLine(
" <tr>");
1353 int colCount = headers?.Count ?? row.Count();
1354 for (
int colIdx = 0; colIdx < colCount; colIdx++)
1356 string cell = (colIdx < row.Count()) ? (row[colIdx].ToString() ??
"") :
"";
1357 cell = cell.Trim(ConverterSettings.InDelimiter.Value);
1358 string safeCell = System.Net.WebUtility.HtmlEncode(cell);
1359 sb.AppendLine($
" <td>{safeCell}</td>");
1362 sb.AppendLine(
" </tr>");
1365 sb.AppendLine(
" </tbody>");
1366 sb.AppendLine(
"</table>");
1368 ConverterSettings.OutputData = sb.ToString();
1372 foreach (Dictionary<object, dynamic> dict
in ConverterSettings.InputDictionary)
1376 if (
true == ConverterSettings.OutputData?.Contains(
"</body>"))
1381 if (dict[
"tag"].ToString() ==
"#text")
1388 stringBuilder.AppendLine(ConverterHelper.ConvertDictionaryToHtml(dict));
1390 if (0 < stringBuilder.Length)
1392 ConverterSettings.OutputData += stringBuilder.ToString();
1393 stringBuilder.Clear();
1399 if (
true == appendToFile)
1400 File.AppendAllText(filename, ConverterSettings.OutputData);
1402 File.WriteAllText(filename, ConverterSettings.OutputData);
1405 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1411 else if (
null != ConverterSettings.OutputDatabase)
1416 IGPALGrid<string> parameterGrid;
1418 if (0 < ConverterSettings.InputGrid.Count &&
null != ConverterSettings.InputGrid[0])
1419 parameterGrid = ConverterSettings.InputGrid[0];
1422 parameterGrid = GPAL.Grid.ToGPALObject();
1424 foreach (
string inputValue
in ConverterSettings.InputData)
1425 parameterGrid.AddRow(
new List<string> { inputValue });
1428 ConverterSettings.OutputDatabase.Create.ClearParameters.WithParameterGrid(parameterGrid).Execute(out
int rowCOunt);
1430 else if (DataFormat.GRID == ConverterSettings.OutDataFormat &&
null != ConverterSettings.OutputGrid
1431 && (
null != ConverterSettings.InputFile ||
true == ConverterSettings.InDataFormat.Contains(DataFormat.GRID)))
1437 foreach (IGPALGrid<string> inGrid
in ConverterSettings.InputGrid)
1438 foreach (List<string> gridRow
in inGrid)
1439 ConverterSettings.OutputGrid.AddRow(
new List<string>(gridRow));
1441 else if (
null != ConverterSettings.OutputClassType)
1443 bool yamlDirectDone =
false;
1444 if (ConverterSettings.InDataFormat.Count > 0 && ConverterSettings.InDataFormat[0] == DataFormat.YAML
1445 && ConverterSettings.InputData.Count > 0)
1450 bool hasAliases =
false;
1451 var preParser =
new Parser(
new System.IO.StringReader(ConverterSettings.InputData[0]));
1452 while (preParser.MoveNext())
1454 if (preParser.Current is AnchorAlias)
1465 var deserializer =
new DeserializerBuilder()
1466 .WithNamingConvention(CamelCaseNamingConvention.Instance)
1467 .WithTypeConverter(
new IPAddressYamlConverter())
1468 .WithTypeConverter(
new ComplexYamlConverter())
1469 .WithTypeConverter(
new UriYamlConverter())
1470 .WithTypeConverter(
new NullableBigIntegerYamlConverter())
1471 .WithTypeConverter(
new NullableGuidYamlConverter())
1472 .WithTypeConverter(
new NullableTimeSpanYamlConverter())
1473 .WithTypeConverter(
new VersionYamlConverter())
1474 .WithTypeConverter(
new DateTimeOffsetYamlConverter())
1475 .WithTypeConverter(
new QuotedStringYamlConverter())
1476 .WithTypeConverter(
new WaitTimeYamlConverter())
1477 .IgnoreUnmatchedProperties()
1480 ConverterSettings.OutputClass = deserializer.Deserialize(ConverterSettings.InputData[0], ConverterSettings.OutputClassType);
1481 yamlDirectDone =
true;
1487 if (!yamlDirectDone)
1492 object classInput = ConverterSettings.InputDictionary;
1493 if (ConverterSettings.InDataFormat.Count > 0 && ConverterSettings.InDataFormat[0] == DataFormat.XML)
1494 classInput = ConverterHelper.StripGpalKeys(ConverterSettings.InputDictionary);
1496 if (
true == ConverterSettings.OutputClassType.IsGenericType &&
true == ConverterHelper.IsDictionaryType(ConverterSettings.OutputClassType) && 0 < ConverterSettings.InputDictionary.Count)
1498 ConverterSettings.OutputClass = ConverterHelper.ConvertToClass(classInput, ConverterSettings.OutputClassType, classInput, ConverterSettings.ColumnNames?[0]);
1500 else if (
true == ConverterSettings.OutputClassType.IsGenericType &&
null != ConverterSettings.OutputClassType.GetInterface(
"IEnumerable"))
1503 if (ConverterSettings.OutputClassElementType == typeof(
string))
1505 var resultList =
new List<string>();
1506 foreach (var dict
in ConverterSettings.InputDictionary)
1508 resultList.AddRange(BuildHierarchicalStrings(dict));
1510 ConverterSettings.OutputClass = resultList;
1512 else if (
true == ConverterSettings.OutputClassElementType.IsArray)
1517 Type elementType = ConverterSettings.OutputClassElementType;
1518 if (elementType ==
null && ConverterSettings.OutputClassType.IsGenericType)
1520 var genericArgs = ConverterSettings.OutputClassType.GetGenericArguments();
1521 elementType = genericArgs.Length > 1 ? genericArgs[1] : genericArgs.FirstOrDefault() ?? typeof(
object);
1523 if (elementType ==
null)
1525 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"OutputClassElementType is null for [{ConverterSettings.OutputClassType}]",
null, GPALObjectType.Other);
1526 ConverterSettings.OutputClass =
default(T);
1530 var mi = typeof(ConverterHelper).GetMethod(
"CreateIEnumerableFromClass",
1531 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1533 new[] { typeof(ConverterSettings) },
1536 var genericMethod = mi.MakeGenericMethod(
new[] { elementType });
1537 ConverterSettings.OutputClass = (T)genericMethod.Invoke(
null,
new object[] { ConverterSettings });
1539 catch (Exception ex)
1541 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to create IEnumerable for [{ConverterSettings.OutputClassType}]: [{ex.Message}]",
null, GPALObjectType.Other, ex);
1542 ConverterSettings.OutputClass =
default(T);
1551 Type elementType = ConverterSettings.OutputClassElementType;
1552 if (elementType ==
null && ConverterSettings.OutputClassType.IsGenericType)
1554 var genericArgs = ConverterSettings.OutputClassType.GetGenericArguments();
1555 elementType = genericArgs.Length > 1 ? genericArgs[1] : genericArgs.FirstOrDefault() ?? typeof(
object);
1557 if (elementType ==
null)
1559 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"OutputClassElementType is null for [{ConverterSettings.OutputClassType}]",
null, GPALObjectType.Other);
1560 ConverterSettings.OutputClass =
default(T);
1565 var mi = typeof(ConverterHelper).GetMethod(
1567 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1569 new[] { typeof(object), typeof(Type), typeof(object), typeof(List<string>) },
1573 ConverterSettings.OutputClass = mi.Invoke(
null,
new object[] { ConverterSettings.InputDictionary, ConverterSettings.OutputClassType, ConverterSettings.InputDictionary, ConverterSettings.ColumnNames?[0] });
1575 catch (Exception ex)
1577 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to create IEnumerable for [{ConverterSettings.OutputClassType}]: [{ex.Message}]",
null, GPALObjectType.Other, ex);
1578 ConverterSettings.OutputClass =
default(T);
1582 MethodInfo addMethod = ConverterSettings.OutputClassType.GetMethod(
"Add",
1583 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
1584 MethodInfo addRowMethod = ConverterSettings.OutputClassType.GetMethod(
"AddRow",
1585 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { typeof(List<>).MakeGenericType(ConverterSettings.OutputClassElementType) },
null);
1586 MethodInfo enqueueMethod = ConverterSettings.OutputClassType.GetMethod(
"Enqueue",
1587 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
1588 MethodInfo pushMethod = ConverterSettings.OutputClassType.GetMethod(
"Push",
1589 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
1595 count = ConverterSettings.OutputClass.Count;
1599 foreach (dynamic item
in ConverterSettings.OutputClass)
1607 if (
null != addRowMethod)
1609 if (
null == ConverterSettings.InputData &&
null != ConverterSettings.InputFile)
1611 string directory = Path.GetDirectoryName(ConverterSettings.InputFile.Filenames[0]);
1612 string filenamepart = Path.GetFileName(ConverterSettings.InputFile.Filenames[0]);
1614 string filename2 = Directory.GetFiles(
false ==
string.IsNullOrEmpty(directory) ? directory :
@".\", filenamepart).FirstOrDefault();
1615 if (
false ==
string.IsNullOrEmpty(filename2))
1619 ConverterSettings.InputData.Add(File.ReadAllText(filename2));
1621 catch (Exception ex)
1623 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to read entire file [{filename2}] into memory. Output may be empty depending on output type.",
this, GPALObjectType.Converter, ex);
1632 foreach (var item
in ConverterSettings.OutputClass)
1634 if (
null != addRowMethod)
1636 addRowMethod.Invoke(outputClass,
new object[] {
new List<string>() { (dynamic)item } });
1650 else if (
null != addMethod ||
null != enqueueMethod ||
null != pushMethod)
1652 Type elementType = outputClass.GetType().IsGenericType ? outputClass.GetType().GetGenericArguments()[0] :
null;
1653 if (elementType !=
null)
1655 foreach (var compatibleItem
in ConvertToCompatibleItems(ConverterSettings.OutputClass, elementType))
1657 if (
null != addMethod)
1658 addMethod.Invoke(outputClass,
new object[] { compatibleItem });
1659 else if (
null != enqueueMethod)
1660 enqueueMethod.Invoke(outputClass,
new object[] { compatibleItem });
1661 else if (
null != pushMethod)
1662 pushMethod.Invoke(outputClass,
new object[] { compatibleItem });
1668 outputClass = (T)(
object)ConverterSettings.OutputClass;
1673 catch (Exception ex)
1675 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Can't find 'Add/Enqueue/Push' method for [{ConverterSettings.OutputClassType}]", ConverterSettings, GPALObjectType.Other, ex);
1681 else if (ConverterSettings.OutputClassType.IsGenericType && ConverterSettings.OutputClassType.GetGenericTypeDefinition() == typeof(List<>))
1684 if (ConverterSettings.OutputClassElementType == typeof(
string))
1686 var resultList =
new List<string>();
1687 foreach (var dict
in ConverterSettings.InputDictionary)
1690 resultList.AddRange(BuildHierarchicalStrings(dict));
1692 ConverterSettings.OutputClass = resultList;
1693 ConverterSettings.OutputClass = (T)(
object)resultList;
1697 var mi = typeof(ConverterHelper).GetMethod(
"CreateListFromClass",
1698 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1700 new[] { typeof(ConverterSettings) },
1702 var fooRef = mi.MakeGenericMethod(
new[] { ConverterSettings.OutputClassElementType });
1703 ConverterSettings.OutputClass = fooRef.Invoke(
new ConverterHelper(),
new object[] { ConverterSettings });
1706 else if (ConverterSettings.OutputClassType.IsArray && ConverterSettings.OutputClassType.GetElementType() == ConverterSettings.OutputClassElementType)
1710 if (ConverterSettings.OutputClassElementType == typeof(
string))
1712 var resultList =
new List<string>();
1713 foreach (var dict
in ConverterSettings.InputDictionary)
1715 foreach (var kvp
in dict)
1717 resultList.Add(kvp.Value?.ToString() ??
string.Empty);
1720 ConverterSettings.OutputClass = resultList;
1724 var mi = typeof(ConverterHelper).GetMethod(
"CreateIEnumerableFromClass",
1725 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1727 new[] { typeof(ConverterSettings) },
1730 var fooRef = mi.MakeGenericMethod(
new[] { ConverterSettings.OutputClassElementType });
1731 ConverterSettings.OutputClass = fooRef.Invoke(
new ConverterHelper(),
new dynamic[] { ConverterSettings });
1738 else if (ConverterSettings.OutputClassType == ConverterSettings.OutputClassType)
1741 if (0 < ConverterSettings.InputDictionary.Count)
1742 ConverterSettings.OutputClass = ConverterHelper.ConvertToClass(classInput, ConverterSettings.OutputClassType, classInput, ConverterSettings.ColumnNames?[0]);
1748 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"Unknown type [{ConverterSettings.OutputClass}].",
this, GPALObjectType.Converter);
1754 if (outputClass != ConverterSettings.OutputClass)
1755 outputClass = (T)ConverterSettings.OutputClass;
1761 else if (ConverterSettings.OutDataFormat == DataFormat.STRING)
1763 outputClass = (T)(
object)ConverterSettings.InputData;
1769 if (
null != ConverterSettings.InputFile)
1771 ((IGPALFileInternal)ConverterSettings.InputFile).FileDictionary.Clear();
1772 ((IGPALFileInternal)ConverterSettings.InputFile).FileData.Clear();
1773 ((IGPALFileInternal)ConverterSettings.InputFile).FileGrid.Clear();
1774 ((IGPALFileInternal)ConverterSettings.InputFile).AlreadyTokenized =
false;
1788 ConverterSettings.RegisteredStructs.Add(knownStructType);
1851 internal static DataFormat GetDataFormat(
char delimiter)
1856 return DataFormat.CARET;
1858 return DataFormat.COLON;
1860 return DataFormat.CSV;
1862 return DataFormat.DOT;
1864 return DataFormat.HYPHEN;
1866 return DataFormat.PIPE;
1868 return DataFormat.SPACE;
1870 return DataFormat.SEMICOLON;
1872 return DataFormat.TAB;
1874 return DataFormat.CUSTOM_DELIMITER;
1885 private List<string> BuildHierarchicalStrings(dynamic dictOrList,
string prefix =
"")
1887 var result =
new List<string>();
1888 if (dictOrList ==
null)
return result;
1891 if (ConverterHelper.IsDictionaryType(dictOrList.GetType()))
1893 foreach (var kvp
in dictOrList)
1895 string key = kvp.Key.ToString();
1896 dynamic value = kvp.Value;
1897 string newPrefix =
string.IsNullOrEmpty(prefix) ?
"" : $
"{prefix}.{key}";
1899 if (ConverterHelper.IsDictionaryType(value.GetType()))
1901 result.AddRange(BuildHierarchicalStrings(value));
1903 else if (ConverterHelper.IsEnumerableType(value.GetType()) && value.GetType() != typeof(
string))
1906 foreach (var item
in value)
1908 string indexedPrefix = $
"{newPrefix}[{index}]";
1909 result.AddRange(BuildHierarchicalStrings(item));
1916 if (
false ==
string.IsNullOrEmpty(newPrefix))
1917 result.Add($
"{newPrefix}={value?.ToString() ?? ""}");
1919 result.Add($
"{value?.ToString() ?? ""}");
1924 else if (ConverterHelper.IsEnumerableType(dictOrList.GetType()) && dictOrList.GetType() != typeof(
string))
1927 foreach (var item
in dictOrList)
1929 string indexedPrefix = $
"{prefix}[{index}]";
1930 result.AddRange(BuildHierarchicalStrings(item, indexedPrefix));
1937 result.Add($
"{prefix}={dictOrList?.ToString() ?? ""}");
1949 private static IEnumerable<object> ConvertToCompatibleItems(
object sourceItem, Type targetType)
1951 var results =
new List<object>();
1953 if (sourceItem ==
null)
1957 if (targetType.IsAssignableFrom(sourceItem.GetType()))
1959 results.Add(sourceItem);
1964 if (sourceItem is IEnumerable enumerable && !(sourceItem is
string))
1966 foreach (var nestedItem
in enumerable)
1968 results.AddRange(ConvertToCompatibleItems(nestedItem, targetType));
1974 if (sourceItem is IDictionary dict)
1976 foreach (var value
in dict.Values)
1978 if (value !=
null && targetType.IsAssignableFrom(value.GetType()))
1982 else if (value !=
null)
1984 results.AddRange(ConvertToCompatibleItems(value, targetType));
1991 var convertedItems =
new List<object>();
1992 var converter = TypeDescriptor.GetConverter(targetType);
1993 if (converter !=
null && converter.CanConvertFrom(sourceItem.GetType()))
1997 var converted = converter.ConvertFrom(sourceItem);
1998 convertedItems.Add(converted);
2005 else if (targetType == typeof(
string))
2009 convertedItems.Add(sourceItem.ToString());
2017 results.AddRange(convertedItems);
2021 internal static class DataFormatDetector
2036 public static DataFormat DetectFormat(
string input)
2038 if (
string.IsNullOrWhiteSpace(input))
2040 GPAL.PublishSimpleEvent(
2041 GPALEventType.WARNING,
2042 "DetectFormat called with empty/null input. Returning NOTSET.",
2043 typeof(DataFormatDetector),
2044 GPALObjectType.Other);
2046 return DataFormat.NOTSET;
2049 string trimmed = input.Trim();
2057 string firstLine = trimmed.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ??
"";
2060 if (trimmed.StartsWith(
"<") && IsLikelyXml(trimmed))
2062 return DataFormat.XML;
2066 if ((trimmed.StartsWith(
"{") || trimmed.StartsWith(
"[")) && IsValidJson(trimmed))
2068 return DataFormat.JSON;
2072 if (!trimmed.StartsWith(
"{") && !trimmed.StartsWith(
"[") && !trimmed.StartsWith(
"<") && IsLikelyYaml(trimmed))
2074 return DataFormat.YAML;
2078 if (trimmed.StartsWith(
"<!DOCTYPE html>") || trimmed.StartsWith(
"<html") || Regex.IsMatch(trimmed,
@"<body|</body|<head|</head", RegexOptions.IgnoreCase))
2080 return DataFormat.HTML;
2091 DataFormat delimFormat = DetectDelimiterFormat(trimmed);
2092 if (delimFormat != DataFormat.NOTSET)
2099 if (Regex.IsMatch(firstLine,
@"^\s*CREATE TABLE|SELECT|INSERT|UPDATE", RegexOptions.IgnoreCase))
2101 return DataFormat.DATABASE;
2103 else if (Regex.IsMatch(trimmed,
@"class\s+\w+\s*{|public\s+class", RegexOptions.IgnoreCase))
2105 return DataFormat.CLASS;
2107 else if (IsKeyValueLike(trimmed,
"="))
2109 return DataFormat.DICTIONARY;
2112 GPAL.PublishSimpleEvent(
2113 GPALEventType.DEBUG,
2114 "No clear DataFormat detected for input starting with [" + firstLine.Substring(0, Math.Min(50, firstLine.Length)) +
"]. Returning STRING.",
2116 GPALObjectType.Other);
2118 return DataFormat.STRING;
2128 private static bool IsLikelyBase64(
string s)
2130 if (
string.IsNullOrEmpty(s))
return false;
2131 if (s.Length < 12)
return false;
2133 string alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2136 foreach (
char c
in s)
2138 if (alphabet.IndexOf(c) == -1)
2145 int mod = s.Length % 4;
2149 if (mod != 2 && mod != 0)
2157 int unique =
new HashSet<char>(s).Count;
2158 if (unique < 5 && s.Length > 20)
2170 private static bool IsLikelyXml(
string input)
2174 using (var reader = XmlReader.Create(
new StringReader(input)))
2176 while (reader.Read()) { }
2191 private static bool IsValidJson(
string input)
2195 JsonConvert.DeserializeObject(input);
2210 private static bool IsLikelyYaml(
string input)
2214 var lines = input.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries);
2215 int yamlPatterns = lines.Count(line => Regex.IsMatch(line.Trim(),
@"^[\w\-]+:\s*(.+|$)") || line.StartsWith(
" ") || line.StartsWith(
"- "));
2216 return yamlPatterns > 2 && lines.Length > 1;
2226 private static DataFormat DetectDelimiterFormat(
string input)
2228 var lines = input.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries)
2232 if (lines.Length < 2)
return DataFormat.NOTSET;
2235 var delimiters =
new Dictionary<char, DataFormat>
2237 {
',', DataFormat.CSV },
2238 {
'\t', DataFormat.TAB },
2239 {
'|', DataFormat.PIPE },
2240 {
';', DataFormat.SEMICOLON },
2241 {
' ', DataFormat.SPACE },
2242 {
'^', DataFormat.CARET },
2243 {
'-', DataFormat.HYPHEN },
2244 {
':', DataFormat.COLON },
2245 {
'.', DataFormat.DOT }
2248 foreach (var kvp
in delimiters)
2250 if (IsConsistentDelimiter(lines, kvp.Key))
2252 if (kvp.Key ==
' ')
return DataFormat.PRN;
2258 if (lines.Select(line => line.Split(
new[] {
',',
';',
'\t',
'|',
' ' }, StringSplitOptions.RemoveEmptyEntries).Length).Distinct().Count() == 1)
2260 return DataFormat.CUSTOM_DELIMITER;
2264 if (lines.All(line => line.Length == lines[0].Length) && Regex.IsMatch(lines[0],
@"[a-zA-Z0-9]+\s+[a-zA-Z0-9]+"))
2266 return DataFormat.GRID;
2269 return DataFormat.NOTSET;
2279 private static bool IsConsistentDelimiter(
string[] lines,
char delim)
2281 var colCounts = lines.Select(line => line.Count(c => c == delim) + 1)
2285 return colCounts.Distinct().Count() == 1 && colCounts[0] > 1 && colCounts[0] == colCounts.Max();
2295 private static bool IsKeyValueLike(
string input,
string separator)
2297 var lines = input.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries);
2298 return lines.Count(line => line.Contains(separator) && line.Split(separator.ToCharArray()).Length == 2) > 2;
2309 private string FormatPretty(
string input)
2311 if (
string.IsNullOrWhiteSpace(input))
2314 var format = ConverterSettings.InDataFormat.FirstOrDefault();
2320 case DataFormat.JSON:
2321 var jsonOptions =
new System.Text.Json.JsonSerializerOptions
2323 WriteIndented =
true,
2324 Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
2326 var jsonElement = System.Text.Json.JsonSerializer.Deserialize<System.Text.Json.JsonElement>(input);
2327 return System.Text.Json.JsonSerializer.Serialize(jsonElement, jsonOptions);
2329 case DataFormat.XML:
2330 var xmlDoc = System.Xml.Linq.XDocument.Parse(input, System.Xml.Linq.LoadOptions.PreserveWhitespace);
2331 return xmlDoc.ToString(System.Xml.Linq.SaveOptions.None);
2333 case DataFormat.HTML:
2337 var htmlDoc =
new HtmlAgilityPack.HtmlDocument();
2338 htmlDoc.LoadHtml(input);
2339 return htmlDoc.DocumentNode.WriteContentTo();
2343 var xDoc = System.Xml.Linq.XDocument.Parse(input, System.Xml.Linq.LoadOptions.PreserveWhitespace);
2344 return xDoc.ToString(System.Xml.Linq.SaveOptions.None);
2347 case DataFormat.YAML:
2348 var deserializer =
new DeserializerBuilder().Build();
2349 var serializer =
new SerializerBuilder()
2350 .WithIndentedSequences()
2351 .WithEventEmitter(next =>
new HumanReadableAliasEventEmitter(next))
2353 var obj = deserializer.Deserialize<
object>(input);
2354 return serializer.Serialize(obj);
2357 case DataFormat.CSV:
2358 case DataFormat.TAB:
2359 case DataFormat.LOG:
2360 case DataFormat.PIPE:
2361 case DataFormat.COLON:
2362 case DataFormat.SEMICOLON:
2363 case DataFormat.SPACE:
2364 case DataFormat.HYPHEN:
2365 case DataFormat.CARET:
2366 case DataFormat.PRN:
2367 case DataFormat.BASE64:
2368 case DataFormat.DATABASE:
2369 case DataFormat.XLSX:
2371 case DataFormat.STRING:
2372 case DataFormat.CLASS:
2373 case DataFormat.DICTIONARY:
2374 case DataFormat.DOT:
2375 case DataFormat.GRID:
2382 catch (Exception ex)
2384 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"PrettyPrint failed for [{format}] [{ex.Message}]");