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));
244 ConverterSettings.OutputFile =
null;
245 ConverterSettings.OutputClass =
null;
246 ConverterSettings.OutputClassType =
null;
247 ConverterSettings.OutputClassElementType =
null;
248 ConverterSettings.OutputGrid =
null;
250 ConverterSettings.OutputData = outputData;
251 if (DataFormat.NOTSET == ConverterSettings.OutDataFormat)
252 ConverterSettings.OutDataFormat = DataFormat.STRING;
253 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Saving [{ConverterSettings.InDataFormat.FirstOrDefault()}] data as [{ConverterSettings.OutDataFormat}] to string.",
this, GPALObjectType.Converter);
254 return Convert(ref outputData);
265 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
266 ConverterSettings.ClearDerived();
267 ConverterSettings.InputClass =
null;
268 ConverterSettings.InputClassType =
null;
269 ConverterSettings.InputClassElementType =
null;
270 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
271 ConverterSettings.ColumnNames =
new GPALGrid<string>();
272 ConverterSettings.InputFile = inputFile;
273 foreach (
string filename
in inputFile.Filenames)
274 ConverterSettings.InDataFormat.Add(
ConverterHelper.GetDataFormatFromExtension(filename));
284 ConverterSettings.OutputFile = outputFile;
286 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Appending [{ConverterSettings.InDataFormat.FirstOrDefault()}] data as [{ConverterSettings.OutDataFormat}] to [{outputFile.Filename}].",
this, GPALObjectType.Converter);
287 return Convert(ref outputFile,
true);
303 ConverterSettings.OutputFile = outputFile;
305 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Saving [{ConverterSettings.InDataFormat.FirstOrDefault()}] data as [{ConverterSettings.OutDataFormat}] to [{outputFile.Filename}].",
this, GPALObjectType.Converter);
306 Convert(ref outputFile);
319 ConverterSettings.InDataFormat =
new System.Collections.Generic.List<
GenerallyPositive.
Enums.DataFormat>();
320 ConverterSettings.ClearDerived();
321 ConverterSettings.InputFirstLineIsColumnHeaders =
false;
322 ConverterSettings.ColumnNames =
new GPALGrid<string>();
323 ConverterSettings.InputFile =
null;
324 ConverterSettings.InputClass = inputClass;
325 ConverterSettings.InputClassType = inputClass.GetType();
326 ConverterSettings.InputClassElementType = ConverterSettings.InputClassType.GetElementType();
327 if (
null == ConverterSettings.InputClassElementType && ConverterSettings.InputClassType.IsGenericType)
329 var genericArgs = ConverterSettings.InputClassType.GetGenericArguments();
330 ConverterSettings.InputClassElementType =
ConverterHelper.IsDictionaryType(ConverterSettings.InputClassType) && genericArgs.Length > 1
332 : genericArgs.FirstOrDefault();
336 ConverterHelper.ConverterSettings = ConverterSettings;
337 ConverterSettings.InputDictionary =
new List<Dictionary<object, dynamic>> {
ConverterHelper.ConvertClassToDictionary(inputClass,
null != ConverterSettings.InputClassElementType ? ConverterSettings.InputClassElementType.ToString() : ConverterSettings.InputClassType.ToString(), visited) };
338 ConverterSettings.InputData.Add(
ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out IGPALGrid<string> outGrid));
339 ConverterSettings.InDataFormat.Add(DataFormat.CLASS);
340 ConverterSettings.InputGrid.Add(outGrid);
346 ConverterSettings.OutputFile =
null;
347 ConverterSettings.OutputClassType = outputClass.GetType();
348 ConverterSettings.OutputClassElementType =
ConverterSettings.OutputClassType.GetElementType();
354 if (genericArgs.Length > 0)
359 ConverterSettings.OutputClassElementType = genericArgs[1];
362 else if (ConverterSettings.OutputClassType.IsGenericType &&
363 ConverterSettings.OutputClassType.GetGenericTypeDefinition() == typeof(IGPALGrid<>))
366 var elementT = genericArgs[0];
367 ConverterSettings.OutputClassElementType = typeof(List<>).MakeGenericType(elementT);
372 ConverterSettings.OutputClassElementType = genericArgs[0];
377 ConverterSettings.OutDataFormat = DataFormat.CLASS;
378 Convert(ref outputClass);
394 if (0 == ConverterSettings.InDataFormat.Count)
395 ConverterSettings.InDataFormat.Add(inDataFormat);
397 ConverterSettings.InDataFormat[ConverterSettings.InDataFormat.Count - 1] = inDataFormat;
408 ConverterSettings.OutDataFormat = outDataFormat;
418 ConverterSettings.FirstLineIsColumnHeaders = firstLineHeaders;
419 ConverterSettings.InputFirstLineIsColumnHeaders = firstLineHeaders;
429 ConverterSettings.IgnoreFirstLineColumnHeaders = ignoreFirstLine;
439 ConverterSettings.FieldsEnclosedInQuotes = fieldsEnclosedInQuotes;
453 ConverterSettings.ColumnNames =
new GPALGrid<string>();
454 if (
null != columnNames && 0 < columnNames.Count)
455 ConverterSettings.ColumnNames.AddRow(
new List<string>(columnNames));
467 if (0 == ConverterSettings.ColumnNames.Count())
468 ConverterSettings.ColumnNames.AddRow(
new List<string>());
469 ConverterSettings.ColumnNames[0].Add(columnName);
480 private List<string> ResolveEffectiveColumns(List<string> sourceColumns)
482 List<string> caller = (
null != ConverterSettings.ColumnNames && 0 <
ConverterSettings.ColumnNames.Count())
485 if (
null == caller || 0 == caller.Count)
486 return (
null != sourceColumns) ?
new List<string>(sourceColumns) : new List<string>();
488 List<string> effective =
new List<string>(caller);
489 if (
null != sourceColumns)
490 for (
int i = effective.Count; i < sourceColumns.Count; i++)
491 effective.Add(sourceColumns[i]);
505 ConverterSettings.OutputFile =
null;
506 ConverterSettings.OutputClass =
null;
507 ConverterSettings.OutputClassType =
null;
508 ConverterSettings.OutputClassElementType =
null;
509 ConverterSettings.OutputGrid =
null;
512 if (ConverterSettings.InDataFormat.FirstOrDefault() != DataFormat.NOTSET)
513 ConverterSettings.OutDataFormat = ConverterSettings.InDataFormat.FirstOrDefault();
515 outputData = FormatPretty(ConverterSettings.InputData.FirstOrDefault());
516 ConverterSettings.OutputData = outputData;
521 #endregion Fluent Interface
533 int InputWithin(
int inputIdx,
int inputCount,
string whatIsMissing)
535 int within = inputIdx;
537 if (inputCount <= inputIdx)
539 within = inputCount - 1;
540 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Output [{inputIdx + 1}] has no {whatIsMissing} of its own, only [{inputCount}] given. Using the last one.",
this, GPALObjectType.Converter);
546 internal IAllowConverterInputAndActions Convert<T>(ref T outputClass,
bool appendToFile =
false)
549 ConverterHelper.ConverterSettings = ConverterSettings;
552 IGPALGrid<string> outGrid = GPAL.Grid.ToGPALObject();
553 ConverterSettings.OutputData =
null;
558 if (
null != ConverterSettings.InputFile)
560 if (
true == File.Exists(ConverterSettings.InputFile))
562 if (
false == FileHelper.TokenizeFile(ConverterSettings.InputFile, ConverterSettings))
564 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to tokenize file [{ConverterSettings.InputFile.Filename}]. Continuing.", ConverterSettings, GPALObjectType.Other);
567 ConverterSettings.InputDictionary = ((IGPALFileInternal)ConverterSettings.InputFile).FileDictionary;
568 ConverterSettings.InputData = ((IGPALFileInternal)ConverterSettings.InputFile).FileData;
569 ConverterSettings.InputGrid = ((IGPALFileInternal)ConverterSettings.InputFile).FileGrid;
572 if (ConverterHelper.IsDelimitedDataFormat(ConverterHelper.GetDataFormatFromExtension(ConverterSettings.InputFile.Filename))
573 && 0 == ConverterSettings.ColumnNames.Count())
575 ConverterSettings.ColumnNames =
new GPALGrid<string>();
576 foreach (List<string> colRow
in ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ColumnList)
577 ConverterSettings.ColumnNames.AddRow(
new List<string>(colRow));
579 ConverterSettings.ExcelRowsPerSheet = ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ExcelRowsPerSheet;
580 ConverterSettings.ExcelSheetNames = ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ExcelSheetNames;
581 ConverterSettings.FirstLineIsColumnHeaders = (bool)(0 < ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.FirstLineIsColumnNames.Count() ? ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.FirstLineIsColumnNames?[0] : ConverterSettings.InputFirstLineIsColumnHeaders);
582 ConverterSettings.InDelimiter = ((IGPALFileInternal)ConverterSettings.InputFile).Delimiter[0];
589 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"File missing: [{ConverterSettings.InputFile.Filename}]", ConverterSettings, GPALObjectType.Other);
591 else if (0 < ConverterSettings.InputData.Count)
593 for (
int idx = 0; idx < ConverterSettings.InputData.Count; idx++)
595 switch (ConverterSettings.InDataFormat[idx])
597 case DataFormat.CUSTOM_DELIMITER:
598 case DataFormat.CARET:
601 case DataFormat.PIPE:
604 ConverterSettings.InputDictionary.Clear();
605 ConverterSettings.InputDictionary.Add(ConverterHelper.ConvertDelimitedToDictionary(ConverterSettings,
new StringReader(ConverterSettings.InputData[idx])));
606 ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid);
607 ConverterSettings.InputGrid.Add(outGrid);
609 case DataFormat.STRING:
610 ConverterSettings.InputGrid.Add(GPAL.Grid.AddRow(
new List<string> { ConverterSettings.InputData[idx] }).ToGPALObject());
611 ConverterSettings.InputDictionary.Clear();
612 ConverterSettings.InputDictionary.Add(ConverterHelper.ConvertDelimitedToDictionary(ConverterSettings,
new StringReader(ConverterSettings.InputData[idx])));
614 case DataFormat.DICTIONARY:
616 case DataFormat.JSON:
617 var desObj = JsonConvert.DeserializeObject<dynamic>(ConverterSettings.InputData[idx],
new JsonConverter[] {
new CustomJsonConverter() });
620 Dictionary<object, dynamic> jsonDict;
621 if (desObj is IDictionary)
623 jsonDict = (Dictionary<object, dynamic>)desObj;
625 else if (desObj is IList jsonList)
629 jsonDict =
new Dictionary<object, dynamic>();
631 foreach (var item
in jsonList)
633 jsonDict[$
"GPALKEY{rootIdx++:D4}"] = item;
639 jsonDict =
new Dictionary<object, dynamic> { [
"GPALKEY0000"] = desObj };
642 ConverterSettings.InputDictionary.Add(jsonDict);
643 ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid);
644 ConverterSettings.InputGrid.Add(outGrid);
652 case DataFormat.XLSX:
655 ConverterSettings.InputXmlDocument.PreserveWhitespace =
true;
656 ConverterSettings.InputXmlDocument.Load(ConverterSettings.InputData[idx]);
657 ConverterSettings.InputDictionary.Add(ConverterHelper.ConvertXMLToDictionary(ConverterSettings.InputXmlDocument));
658 ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid);
659 ConverterSettings.InputGrid.Add(outGrid);
661 case DataFormat.YAML:
662 var deserializer =
new DeserializerBuilder()
663 .WithNamingConvention(CamelCaseNamingConvention.Instance)
666 dynamic yamlObject = deserializer.Deserialize<
object>(ConverterSettings.InputData[idx]);
667 var yamlGridRows =
new List<List<string>>();
668 var processedYamlDict =
new Dictionary<dynamic, dynamic>();
670 if (yamlObject is IDictionary yamlMapping)
672 foreach (var key
in yamlMapping.Keys)
674 string keyStr = key?.ToString() ??
"null_key";
675 processedYamlDict[keyStr] = FileHelper.ProcessDictionaryValue(yamlMapping[key], ConverterSettings, yamlGridRows);
678 else if (yamlObject is IList yamlSequence)
683 foreach (var item
in yamlSequence)
685 processedYamlDict[$
"GPALKEY{rootIdx++:D4}"] = FileHelper.ProcessDictionaryValue(item, ConverterSettings, yamlGridRows);
691 processedYamlDict[
"GPALKEY0000"] = FileHelper.ProcessDictionaryValue(yamlObject, ConverterSettings, yamlGridRows);
694 var yamlGridRow =
new GPALGrid<string>();
695 foreach (var row
in yamlGridRows)
697 yamlGridRow.AddRow(row);
700 ConverterSettings.InputDictionary.Add(processedYamlDict);
701 ConverterSettings.InputGrid.Add(yamlGridRow);
703 case DataFormat.HTML:
704 ConverterSettings.InputDictionary = ConverterHelper.ConvertHtmlToDictionary(ConverterSettings.InputData[idx]);
705 ConverterSettings.InputData.Add(ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid));
706 ConverterSettings.InputGrid.Add(outGrid);
711 else if (
null != ConverterSettings.InputDatabase)
713 List<string> dbColumns =
new List<string>();
716 DatabaseHelper.TokenizeDatabase(
null, ConverterSettings.InputDatabase);
717 ConverterSettings.InputGrid.Add(ConverterSettings.InputDatabase.Tokens);
718 if (0 < ((GPALDatabase)ConverterSettings.InputDatabase).DatabaseSettings.ColumnList.Count)
719 foreach (DatabaseSettings.DatabaseColumn column in ((GPALDatabase)ConverterSettings.InputDatabase).DatabaseSettings.ColumnList)
720 dbColumns.Add(column.ColumName);
724 List<string> columns = ResolveEffectiveColumns(dbColumns);
725 if (0 == ConverterSettings.ColumnNames.Count() && 0 < columns.Count)
726 ConverterSettings.ColumnNames.AddRow(
new List<string>(columns));
728 ConverterSettings.InputDictionary = ConverterHelper.ConvertGridToDictionary(columns, ConverterSettings.InputDatabase.Tokens);
730 else if (
null != ConverterSettings.InputGrid && 0 < ConverterSettings.InputGrid.Count)
735 List<string> columns = ResolveEffectiveColumns(
null);
736 bool haveColumns = 0 < columns.Count;
738 ConverterSettings.InputDictionary.Clear();
739 foreach (IGPALGrid<string> inGrid
in ConverterSettings.InputGrid)
743 Dictionary<object, dynamic> source =
new Dictionary<object, dynamic>();
745 foreach (List<string> gridRow
in inGrid)
749 Dictionary<string, string> rowDict =
new Dictionary<string, string>();
750 for (
int i = 0; i < gridRow.Count; i++)
751 rowDict[i < columns.Count ? columns[i] : $
"Column{i + 1}"] = gridRow[i];
752 source[$
"GPALKEY{lineNumber:D4}"] = rowDict;
755 source[$
"GPALKEY{lineNumber:D4}"] =
new List<string>(gridRow);
758 ConverterSettings.InputDictionary.Add(source);
761 else if (
null != ConverterSettings.InputClass && 0 < ConverterSettings.InDataFormat.Count)
764 HashSet<object> visited =
new HashSet<object>(ConverterHelper.ReferenceEqualityComparer.Instance);
765 IGPALGrid<string> repopGrid;
766 ConverterSettings.InputDictionary =
new List<Dictionary<object, dynamic>> {
767 ConverterHelper.ConvertClassToDictionary(
768 ConverterSettings.InputClass,
769 null != ConverterSettings.InputClassElementType
770 ? ConverterSettings.InputClassElementType.ToString()
771 : ConverterSettings.InputClassType.ToString(),
774 ConverterSettings.InputData.Add(ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out repopGrid));
775 ConverterSettings.InDataFormat.Add(DataFormat.CLASS);
776 ConverterSettings.InputGrid.Add(repopGrid);
780 StringBuilder stringBuilder =
new StringBuilder();
781 if (
null != ConverterSettings.OutputFile)
787 int inputFileCount = Math.Max(1, ConverterSettings.InDataFormat.Count);
788 GPALFileSettings outputFileSettings = ((IGPALFileInternal)ConverterSettings.OutputFile).FileSettings;
793 List<string> outputPaths =
new List<string>(outputFileSettings.Filenames);
795 if (0 < outputPaths.Count && outputPaths.Count < inputFileCount)
797 string pattern = outputPaths[outputPaths.Count - 1];
798 string patternDirectory = Path.GetDirectoryName(pattern);
799 string patternName = Path.GetFileNameWithoutExtension(pattern);
800 string patternExtension = Path.GetExtension(pattern);
802 for (
int newFileIdx = outputPaths.Count; newFileIdx < inputFileCount; newFileIdx++)
804 string generatedFilename = Path.Combine(patternDirectory, $
"{patternName}_{newFileIdx + 1}{patternExtension}");
805 outputPaths.Add(generatedFilename);
806 ConverterSettings.OutputFile.ReturnFilenames.Add(generatedFilename);
811 DataFormat savedOutDataFormat = ConverterSettings.OutDataFormat;
812 foreach (
string filename
in outputPaths)
814 int inputIdx = Math.Min(fileIdx, inputFileCount - 1);
817 if (
null != ConverterSettings.InputFile && inputIdx < ((IGPALFileInternal)ConverterSettings.InputFile).Delimiter.Count)
818 ConverterSettings.InDelimiter = ((IGPALFileInternal)ConverterSettings.InputFile).Delimiter[inputIdx];
821 if (DataFormat.NOTSET == savedOutDataFormat)
822 ConverterSettings.OutDataFormat = ConverterHelper.GetDataFormatFromExtension(filename);
829 if (fileIdx < ((IGPALFileInternal)ConverterSettings.OutputFile).FirstLineIsColumnNames.Count)
832 ConverterSettings.FirstLineIsColumnHeaders = ((IGPALFileInternal)ConverterSettings.OutputFile).FirstLineIsColumnNames[fileIdx];
837 ConverterSettings.FirstLineIsColumnHeaders =
838 (
null != ConverterSettings.ColumnNames && 0 < ConverterSettings.ColumnNames.Count())
839 || ConverterSettings.InputFirstLineIsColumnHeaders;
842 if (
true == appendToFile)
843 ConverterSettings.FirstLineIsColumnHeaders =
false;
845 if (fileIdx < ((IGPALFileInternal)ConverterSettings.OutputFile).FieldsEnclosedInQuotes.Count)
846 ConverterSettings.FieldsEnclosedInQuotes = ((IGPALFileInternal)ConverterSettings.OutputFile).FieldsEnclosedInQuotes[fileIdx];
848 if (
null == ConverterSettings.OutDelimiter)
849 ConverterSettings.OutDelimiter = ConverterHelper.GetDelimiterFromFormat(ConverterSettings.OutDataFormat);
851 if (
null != ConverterSettings.ColumnNames && 0 == ConverterSettings.ColumnNames.Count())
853 List<string> columns =
new List<string>();
855 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())
856 foreach (
string columnName
in ((IGPALFileInternal)ConverterSettings.OutputFile).FileSettings.ColumnList[fileIdx])
857 columns.Add(columnName);
858 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())
859 foreach (
string columnName
in ((IGPALFileInternal)ConverterSettings.InputFile).FileSettings.ColumnList[inputIdx])
860 columns.Add(columnName);
862 if (0 < columns.Count)
863 ConverterSettings.ColumnNames.AddRow(columns);
866 switch (ConverterSettings.OutDataFormat)
868 case DataFormat.CUSTOM_DELIMITER:
869 case DataFormat.CARET:
872 case DataFormat.PIPE:
875 case DataFormat.NOTSET:
876 if (0 < ConverterSettings.InDataFormat.Count)
878 switch (ConverterSettings.InDataFormat[inputIdx])
880 case DataFormat.CARET:
881 case DataFormat.COLON:
883 case DataFormat.CUSTOM_DELIMITER:
886 case DataFormat.HYPHEN:
888 case DataFormat.PIPE:
889 case DataFormat.SEMICOLON:
890 case DataFormat.SPACE:
893 if (inputIdx < ConverterSettings.InputData.Count)
894 ConverterSettings.OutputData = ConverterSettings.InputData[inputIdx];
896 ConverterSettings.OutputData = File.ReadAllText(ConverterSettings.InputFile.Filenames[inputIdx]);
897 if (
null != ConverterSettings.OutputData &&
null != ConverterSettings.InDelimiter &&
true == ConverterSettings.InDelimiter.HasValue)
899 using (StringReader reader =
new StringReader(ConverterSettings.OutputData))
902 StringBuilder lineBuilder =
new StringBuilder();
903 while ((line = reader.ReadLine()) !=
null)
905 string[] tokens = line.Split(ConverterSettings.InDelimiter.Value);
906 foreach (
string str
in tokens)
907 stringBuilder.Append(ConverterHelper.CreateCSVToken(str, ConverterSettings));
908 if (0 < stringBuilder.Length)
909 stringBuilder.Remove(stringBuilder.Length - 1, 1);
910 lineBuilder.AppendLine(stringBuilder.ToString());
911 stringBuilder.Clear();
913 ConverterSettings.OutputData = lineBuilder.ToString();
916 stringBuilder.Append(ConverterSettings.OutputData);
918 case DataFormat.HTML:
921 case DataFormat.CLASS:
923 stringBuilder.Append(ConverterHelper.ConvertInputDictionaryToDelimitedAndGrid(ConverterSettings, out outGrid));
926 if (
true == appendToFile)
927 File.AppendAllText(filename, stringBuilder.ToString());
929 File.WriteAllText(filename, stringBuilder.ToString());
932 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"File [{ConverterSettings.InputFile}] does not exist(?) as no DATA FORMAT has been set.",
this, GPALObjectType.Converter);
934 case DataFormat.JSON:
935 if (ConverterSettings.InputDictionary.Count > 0)
937 int dictIdx = InputWithin(inputIdx, ConverterSettings.InputDictionary.Count,
"input");
938 var rootDict = ConverterSettings.InputDictionary[dictIdx] as IDictionary;
942 if (rootDict !=
null && ConverterHelper.IsArrayLike(rootDict))
944 var list =
new List<object>();
945 foreach (DictionaryEntry e
in rootDict)
947 string k = e.Key.ToString();
948 if (k.StartsWith(
"GPALKEY"))
957 rawObject = rootDict ?? ConverterSettings.InputDictionary[dictIdx];
960 dynamic cleanObject = ConverterHelper.NormalizeForCleanJson(rawObject);
961 cleanObject = ConverterHelper.StripGpalKeys(cleanObject);
963 var settings =
new JsonSerializerSettings
968 new IPAddressJsonConverter(),
969 new ComplexJsonConverter(),
970 new UriJsonConverter(),
971 new VersionJsonConverter(),
975 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
976 Formatting = Newtonsoft.Json.Formatting.Indented
979 ConverterSettings.OutputData = JsonConvert.SerializeObject(cleanObject, settings);
982 File.AppendAllText(filename, ConverterSettings.OutputData);
984 File.WriteAllText(filename, ConverterSettings.OutputData);
987 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
991 case DataFormat.XLSX:
992 if (inputIdx < ConverterSettings.InputGrid.Count)
993 ExcelHelper.WriteXlsxFromTokenizedData(
994 outputPath: filename,
995 grid: ConverterSettings.InputGrid[inputIdx],
996 rowsPerSheet: ConverterSettings.ExcelRowsPerSheet[inputIdx],
997 columnNamesPerSheet: ConverterSettings.ColumnNames,
998 sheetNames: ConverterSettings.ExcelSheetNames[inputIdx],
999 writeHeaders: ConverterSettings.FirstLineIsColumnHeaders
1002 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1004 case DataFormat.XML:
1005 if (0 < ConverterSettings.InputDictionary.Count)
1007 ConverterSettings.OutputXmlDocument =
new XmlDocument();
1010 var rootElement = ConverterSettings.OutputXmlDocument.CreateElement(
"root");
1011 ConverterSettings.OutputXmlDocument.AppendChild(rootElement);
1014 if (
true == appendToFile)
1016 XmlDocument xmlDocument =
new XmlDocument();
1019 xmlDocument.Load(filename);
1020 ConverterSettings.OutputXmlDocument = xmlDocument;
1021 rootElement = (XmlElement)xmlDocument.GetElementsByTagName(
"root")[0];
1023 catch (Exception ex)
1025 GPAL.PublishSimpleEvent(GPALEventType.INFO, $
"File [{filename}] does not exist to append XML to, writing new.",
this, GPALObjectType.Converter, ex);
1030 foreach (Dictionary<object, dynamic> dict
in ConverterSettings.InputDictionary)
1032 if (DataFormat.HTML == ConverterSettings.InDataFormat[dictCount])
1033 ConverterHelper.ConvertHtmlDictionaryToXml(dict, rootElement);
1034 else if (DataFormat.XML == ConverterSettings.InDataFormat[dictCount])
1035 ConverterHelper.ConvertDictionaryToXmlElement(dict, rootElement,
false);
1037 ConverterHelper.ConvertDictionaryToXmlElement(dict, rootElement);
1042 string inputFilename = ConverterSettings.InputFile?.Filename;
1044 if (
true == ConverterSettings.InputFile?.Filename.ToLower().EndsWith(
"xml"))
1046 string header = ConverterHelper.GetXmlHeaderFromFile(inputFilename);
1049 if (0 < header.Length)
1050 using (var writer =
new StreamWriter(filename, append:
false))
1052 writer.Write(header);
1054 using (var xmlWriter = XmlWriter.Create(writer,
new XmlWriterSettings
1056 OmitXmlDeclaration =
true,
1058 Encoding = writer.Encoding
1061 ConverterSettings.OutputXmlDocument.Save(xmlWriter);
1065 ConverterSettings.OutputXmlDocument.Save(filename);
1068 ConverterSettings.OutputXmlDocument.Save(filename);
1071 case DataFormat.YAML:
1072 if (ConverterSettings.InputClass !=
null)
1074 var serializer =
new SerializerBuilder()
1075 .WithObjectGraphTraversalStrategyFactory((ti, tr, tc, depth)
1076 =>
new FullObjectGraphTraversalStrategy(ti, tr, depth, CamelCaseNamingConvention.Instance,
new DefaultObjectFactory()))
1077 .WithNamingConvention(CamelCaseNamingConvention.Instance)
1078 .WithEventEmitter(next =>
new ExplicitNullYamlEventEmitter(next))
1079 .WithEventEmitter(next =>
new HumanReadableAliasEventEmitter(next))
1080 .WithTypeConverter(
new IPAddressYamlConverter())
1081 .WithTypeConverter(
new ComplexYamlConverter())
1082 .WithTypeConverter(
new UriYamlConverter())
1083 .WithTypeConverter(
new NullableBigIntegerYamlConverter())
1084 .WithTypeConverter(
new NullableGuidYamlConverter())
1085 .WithTypeConverter(
new NullableTimeSpanYamlConverter())
1086 .WithTypeConverter(
new VersionYamlConverter())
1087 .WithTypeConverter(
new DateTimeOffsetYamlConverter())
1088 .WithTypeConverter(
new QuotedStringYamlConverter())
1089 .WithTypeConverter(
new WaitTimeYamlConverter())
1092 ConverterSettings.OutputData = serializer.Serialize(ConverterSettings.InputClass);
1094 if (
true == appendToFile)
1095 File.AppendAllText(filename, ConverterSettings.OutputData);
1097 File.WriteAllText(filename, ConverterSettings.OutputData);
1099 else if (0 < ConverterSettings.InputDictionary.Count)
1106 const int aliasGraphLimit = 5000;
1107 int yamlIdx = InputWithin(inputIdx, ConverterSettings.InputDictionary.Count,
"input");
1108 bool largeGraph = ConverterHelper.CountGraphNodes(ConverterSettings.InputDictionary[yamlIdx], aliasGraphLimit) >= aliasGraphLimit;
1110 var serializerBuilder =
new SerializerBuilder()
1111 .WithEventEmitter(next =>
new ExplicitNullYamlEventEmitter(next))
1112 .WithEventEmitter(next =>
new HumanReadableAliasEventEmitter(next))
1113 .WithNamingConvention(CamelCaseNamingConvention.Instance)
1114 .WithTypeConverter(
new IPAddressYamlConverter())
1115 .WithTypeConverter(
new ComplexYamlConverter())
1116 .WithTypeConverter(
new UriYamlConverter())
1117 .WithTypeConverter(
new NullableBigIntegerYamlConverter())
1118 .WithTypeConverter(
new NullableGuidYamlConverter())
1119 .WithTypeConverter(
new NullableTimeSpanYamlConverter())
1120 .WithTypeConverter(
new VersionYamlConverter())
1121 .WithTypeConverter(
new DateTimeOffsetYamlConverter())
1122 .WithTypeConverter(
new QuotedStringYamlConverter())
1123 .WithTypeConverter(
new WaitTimeYamlConverter());
1125 if (
true == largeGraph)
1126 serializerBuilder = serializerBuilder.DisableAliases();
1128 var serializer = serializerBuilder.Build();
1130 object CleanForYaml(
object input)
1132 if (input is Dictionary<object, dynamic> d)
1135 bool isXmlDerived = d.Keys.Cast<
object>().Any(k =>
1136 k.ToString() ==
"#text" || k.ToString().StartsWith(
"@"));
1138 if (ConverterHelper.IsArrayLike(d))
1141 var list =
new List<object>();
1142 foreach (var p
in d)
1144 string k = p.Key.ToString();
1146 if (k.StartsWith(
"GPALKEY"))
1148 list.Add(CleanForYaml(p.Value));
1156 var cleanDict =
new Dictionary<object, dynamic>();
1158 string textValue =
null;
1159 if (d.TryGetValue(
"#text", out var txt) && isXmlDerived)
1161 textValue = txt?.ToString();
1165 foreach (var p
in d)
1167 string k = p.Key.ToString();
1169 k = k.Replace(
@"GPAL_",
"");
1171 if (k.StartsWith(
"@") && isXmlDerived)
1173 cleanDict[k.Substring(1)] = p.Value;
1178 foreach (var p
in d)
1180 string k = p.Key.ToString();
1182 k = k.Replace(
@"GPAL_",
"");
1184 if (k ==
"#text" || k.StartsWith(
"@") || k.StartsWith(
"GPALKEY"))
1187 cleanDict[k] = CleanForYaml(p.Value);
1191 if (isXmlDerived && textValue !=
null)
1193 if (cleanDict.Count == 0)
1196 if (
double.TryParse(textValue, out
double num))
1203 cleanDict[
"value"] = textValue;
1210 else if (input is IEnumerable enumerable && !(input is
string))
1212 var list =
new List<object>();
1213 foreach (var item
in enumerable)
1215 list.Add(CleanForYaml(item));
1223 Dictionary<object, dynamic> rootValue = ConverterSettings.InputDictionary[yamlIdx];
1224 dynamic cleanRoot = CleanForYaml(rootValue);
1225 cleanRoot = ConverterHelper.StripGpalKeys(cleanRoot);
1227 if (
true == ConverterHelper.IsEnumerableObject(cleanRoot) && !(cleanRoot is IDictionary) && !(cleanRoot is
string))
1229 cleanRoot =
new Dictionary<object, object>
1231 [
"values"] = cleanRoot
1235 ConverterSettings.OutputData = serializer.Serialize(cleanRoot);
1237 if (
true == appendToFile)
1238 File.AppendAllText(filename, ConverterSettings.OutputData);
1240 File.WriteAllText(filename, ConverterSettings.OutputData);
1243 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1245 case DataFormat.HTML:
1246 if (0 < ConverterSettings.InputDictionary.Count)
1248 switch (ConverterSettings.InDataFormat[inputIdx])
1250 case DataFormat.CARET:
1251 case DataFormat.COLON:
1252 case DataFormat.CSV:
1253 case DataFormat.CUSTOM_DELIMITER:
1254 case DataFormat.DOT:
1255 case DataFormat.HYPHEN:
1256 case DataFormat.LOG:
1257 case DataFormat.PIPE:
1258 case DataFormat.SEMICOLON:
1259 case DataFormat.SPACE:
1260 case DataFormat.TAB:
1261 case DataFormat.PRN:
1263 var sb =
new StringBuilder();
1265 var grid = ConverterSettings.InputGrid;
1266 if (grid ==
null || grid.Count == 0)
1268 ConverterSettings.OutputData =
"<p>No data</p>";
1272 int gridIdx = InputWithin(inputIdx, grid.Count,
"input grid");
1274 sb.AppendLine(
"<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse: collapse; font-family: Arial, sans-serif; width: 100%;\">");
1277 List<string> headers =
null;
1278 int dataStartRow = 0;
1280 if (ConverterSettings.ColumnNames?.Count() > fileIdx && ConverterSettings.ColumnNames[fileIdx].Count > 0)
1282 headers = ConverterSettings.ColumnNames[fileIdx];
1285 else if (ConverterSettings.FirstLineIsColumnHeaders && grid.Count > 0)
1287 headers = grid[gridIdx][0];
1292 if (headers !=
null)
1294 sb.AppendLine(
" <thead>");
1295 sb.AppendLine(
" <tr>");
1296 foreach (var h
in headers)
1298 string safeHeader = System.Net.WebUtility.HtmlEncode(h ??
"");
1299 sb.AppendLine($
" <th>{safeHeader}</th>");
1301 sb.AppendLine(
" </tr>");
1302 sb.AppendLine(
" </thead>");
1306 sb.AppendLine(
" <tbody>");
1308 for (
int rowIdx = dataStartRow; rowIdx < grid[gridIdx].Count(); rowIdx++)
1310 var row = grid[gridIdx][rowIdx];
1311 sb.AppendLine(
" <tr>");
1314 int colCount = headers?.Count ?? row.Count();
1315 for (
int colIdx = 0; colIdx < colCount; colIdx++)
1317 string cell = (colIdx < row.Count()) ? (row[colIdx].ToString() ??
"") :
"";
1318 cell = cell.Trim(ConverterSettings.InDelimiter.Value);
1319 string safeCell = System.Net.WebUtility.HtmlEncode(cell);
1320 sb.AppendLine($
" <td>{safeCell}</td>");
1323 sb.AppendLine(
" </tr>");
1326 sb.AppendLine(
" </tbody>");
1327 sb.AppendLine(
"</table>");
1329 ConverterSettings.OutputData = sb.ToString();
1333 foreach (Dictionary<object, dynamic> dict
in ConverterSettings.InputDictionary)
1337 if (
true == ConverterSettings.OutputData?.Contains(
"</body>"))
1342 if (dict[
"tag"].ToString() ==
"#text")
1349 stringBuilder.AppendLine(ConverterHelper.ConvertDictionaryToHtml(dict));
1351 if (0 < stringBuilder.Length)
1353 ConverterSettings.OutputData += stringBuilder.ToString();
1354 stringBuilder.Clear();
1360 if (
true == appendToFile)
1361 File.AppendAllText(filename, ConverterSettings.OutputData);
1363 File.WriteAllText(filename, ConverterSettings.OutputData);
1366 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No input data to write to [{filename}]",
this, GPALObjectType.Converter);
1372 else if (
null != ConverterSettings.OutputDatabase)
1377 IGPALGrid<string> parameterGrid;
1379 if (0 < ConverterSettings.InputGrid.Count &&
null != ConverterSettings.InputGrid[0])
1380 parameterGrid = ConverterSettings.InputGrid[0];
1383 parameterGrid = GPAL.Grid.ToGPALObject();
1385 foreach (
string inputValue
in ConverterSettings.InputData)
1386 parameterGrid.AddRow(
new List<string> { inputValue });
1389 ConverterSettings.OutputDatabase.Create.ClearParameters.WithParameterGrid(parameterGrid).Execute(out
int rowCOunt);
1391 else if (DataFormat.GRID == ConverterSettings.OutDataFormat &&
null != ConverterSettings.OutputGrid
1392 && (
null != ConverterSettings.InputFile ||
true == ConverterSettings.InDataFormat.Contains(DataFormat.GRID)))
1398 foreach (IGPALGrid<string> inGrid
in ConverterSettings.InputGrid)
1399 foreach (List<string> gridRow
in inGrid)
1400 ConverterSettings.OutputGrid.AddRow(
new List<string>(gridRow));
1402 else if (
null != ConverterSettings.OutputClassType)
1404 bool yamlDirectDone =
false;
1405 if (ConverterSettings.InDataFormat.Count > 0 && ConverterSettings.InDataFormat[0] == DataFormat.YAML
1406 && ConverterSettings.InputData.Count > 0)
1411 bool hasAliases =
false;
1412 var preParser =
new Parser(
new System.IO.StringReader(ConverterSettings.InputData[0]));
1413 while (preParser.MoveNext())
1415 if (preParser.Current is AnchorAlias)
1426 var deserializer =
new DeserializerBuilder()
1427 .WithNamingConvention(CamelCaseNamingConvention.Instance)
1428 .WithTypeConverter(
new IPAddressYamlConverter())
1429 .WithTypeConverter(
new ComplexYamlConverter())
1430 .WithTypeConverter(
new UriYamlConverter())
1431 .WithTypeConverter(
new NullableBigIntegerYamlConverter())
1432 .WithTypeConverter(
new NullableGuidYamlConverter())
1433 .WithTypeConverter(
new NullableTimeSpanYamlConverter())
1434 .WithTypeConverter(
new VersionYamlConverter())
1435 .WithTypeConverter(
new DateTimeOffsetYamlConverter())
1436 .WithTypeConverter(
new QuotedStringYamlConverter())
1437 .WithTypeConverter(
new WaitTimeYamlConverter())
1438 .IgnoreUnmatchedProperties()
1441 ConverterSettings.OutputClass = deserializer.Deserialize(ConverterSettings.InputData[0], ConverterSettings.OutputClassType);
1442 yamlDirectDone =
true;
1448 if (!yamlDirectDone)
1453 object classInput = ConverterSettings.InputDictionary;
1454 if (ConverterSettings.InDataFormat.Count > 0 && ConverterSettings.InDataFormat[0] == DataFormat.XML)
1455 classInput = ConverterHelper.StripGpalKeys(ConverterSettings.InputDictionary);
1457 if (
true == ConverterSettings.OutputClassType.IsGenericType &&
true == ConverterHelper.IsDictionaryType(ConverterSettings.OutputClassType) && 0 < ConverterSettings.InputDictionary.Count)
1459 ConverterSettings.OutputClass = ConverterHelper.ConvertToClass(classInput, ConverterSettings.OutputClassType, classInput, ConverterSettings.ColumnNames?[0]);
1461 else if (
true == ConverterSettings.OutputClassType.IsGenericType &&
null != ConverterSettings.OutputClassType.GetInterface(
"IEnumerable"))
1464 if (ConverterSettings.OutputClassElementType == typeof(
string))
1466 var resultList =
new List<string>();
1467 foreach (var dict
in ConverterSettings.InputDictionary)
1469 resultList.AddRange(BuildHierarchicalStrings(dict));
1471 ConverterSettings.OutputClass = resultList;
1473 else if (
true == ConverterSettings.OutputClassElementType.IsArray)
1478 Type elementType = ConverterSettings.OutputClassElementType;
1479 if (elementType ==
null && ConverterSettings.OutputClassType.IsGenericType)
1481 var genericArgs = ConverterSettings.OutputClassType.GetGenericArguments();
1482 elementType = genericArgs.Length > 1 ? genericArgs[1] : genericArgs.FirstOrDefault() ?? typeof(
object);
1484 if (elementType ==
null)
1486 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"OutputClassElementType is null for [{ConverterSettings.OutputClassType}]",
null, GPALObjectType.Other);
1487 ConverterSettings.OutputClass =
default(T);
1491 var mi = typeof(ConverterHelper).GetMethod(
"CreateIEnumerableFromClass",
1492 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1494 new[] { typeof(ConverterSettings) },
1497 var genericMethod = mi.MakeGenericMethod(
new[] { elementType });
1498 ConverterSettings.OutputClass = (T)genericMethod.Invoke(
null,
new object[] { ConverterSettings });
1500 catch (Exception ex)
1502 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to create IEnumerable for [{ConverterSettings.OutputClassType}]: [{ex.Message}]",
null, GPALObjectType.Other, ex);
1503 ConverterSettings.OutputClass =
default(T);
1512 Type elementType = ConverterSettings.OutputClassElementType;
1513 if (elementType ==
null && ConverterSettings.OutputClassType.IsGenericType)
1515 var genericArgs = ConverterSettings.OutputClassType.GetGenericArguments();
1516 elementType = genericArgs.Length > 1 ? genericArgs[1] : genericArgs.FirstOrDefault() ?? typeof(
object);
1518 if (elementType ==
null)
1520 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"OutputClassElementType is null for [{ConverterSettings.OutputClassType}]",
null, GPALObjectType.Other);
1521 ConverterSettings.OutputClass =
default(T);
1526 var mi = typeof(ConverterHelper).GetMethod(
1528 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1530 new[] { typeof(object), typeof(Type), typeof(object), typeof(List<string>) },
1534 ConverterSettings.OutputClass = mi.Invoke(
null,
new object[] { ConverterSettings.InputDictionary, ConverterSettings.OutputClassType, ConverterSettings.InputDictionary, ConverterSettings.ColumnNames?[0] });
1536 catch (Exception ex)
1538 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to create IEnumerable for [{ConverterSettings.OutputClassType}]: [{ex.Message}]",
null, GPALObjectType.Other, ex);
1539 ConverterSettings.OutputClass =
default(T);
1543 MethodInfo addMethod = ConverterSettings.OutputClassType.GetMethod(
"Add",
1544 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
1545 MethodInfo addRowMethod = ConverterSettings.OutputClassType.GetMethod(
"AddRow",
1546 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { typeof(List<>).MakeGenericType(ConverterSettings.OutputClassElementType) },
null);
1547 MethodInfo enqueueMethod = ConverterSettings.OutputClassType.GetMethod(
"Enqueue",
1548 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
1549 MethodInfo pushMethod = ConverterSettings.OutputClassType.GetMethod(
"Push",
1550 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
1556 count = ConverterSettings.OutputClass.Count;
1560 foreach (dynamic item
in ConverterSettings.OutputClass)
1568 if (
null != addRowMethod)
1570 if (
null == ConverterSettings.InputData &&
null != ConverterSettings.InputFile)
1572 string directory = Path.GetDirectoryName(ConverterSettings.InputFile.Filenames[0]);
1573 string filenamepart = Path.GetFileName(ConverterSettings.InputFile.Filenames[0]);
1575 string filename2 = Directory.GetFiles(
false ==
string.IsNullOrEmpty(directory) ? directory :
@".\", filenamepart).FirstOrDefault();
1576 if (
false ==
string.IsNullOrEmpty(filename2))
1580 ConverterSettings.InputData.Add(File.ReadAllText(filename2));
1582 catch (Exception ex)
1584 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to read entire file [{filename2}] into memory. Output may be empty depending on output type.",
this, GPALObjectType.Converter, ex);
1593 foreach (var item
in ConverterSettings.OutputClass)
1595 if (
null != addRowMethod)
1597 addRowMethod.Invoke(outputClass,
new object[] {
new List<string>() { (dynamic)item } });
1611 else if (
null != addMethod ||
null != enqueueMethod ||
null != pushMethod)
1613 Type elementType = outputClass.GetType().IsGenericType ? outputClass.GetType().GetGenericArguments()[0] :
null;
1614 if (elementType !=
null)
1616 foreach (var compatibleItem
in ConvertToCompatibleItems(ConverterSettings.OutputClass, elementType))
1618 if (
null != addMethod)
1619 addMethod.Invoke(outputClass,
new object[] { compatibleItem });
1620 else if (
null != enqueueMethod)
1621 enqueueMethod.Invoke(outputClass,
new object[] { compatibleItem });
1622 else if (
null != pushMethod)
1623 pushMethod.Invoke(outputClass,
new object[] { compatibleItem });
1629 outputClass = (T)(
object)ConverterSettings.OutputClass;
1634 catch (Exception ex)
1636 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Can't find 'Add/Enqueue/Push' method for [{ConverterSettings.OutputClassType}]", ConverterSettings, GPALObjectType.Other, ex);
1642 else if (ConverterSettings.OutputClassType.IsGenericType && ConverterSettings.OutputClassType.GetGenericTypeDefinition() == typeof(List<>))
1645 if (ConverterSettings.OutputClassElementType == typeof(
string))
1647 var resultList =
new List<string>();
1648 foreach (var dict
in ConverterSettings.InputDictionary)
1651 resultList.AddRange(BuildHierarchicalStrings(dict));
1653 ConverterSettings.OutputClass = resultList;
1654 ConverterSettings.OutputClass = (T)(
object)resultList;
1658 var mi = typeof(ConverterHelper).GetMethod(
"CreateListFromClass",
1659 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1661 new[] { typeof(ConverterSettings) },
1663 var fooRef = mi.MakeGenericMethod(
new[] { ConverterSettings.OutputClassElementType });
1664 ConverterSettings.OutputClass = fooRef.Invoke(
new ConverterHelper(),
new object[] { ConverterSettings });
1667 else if (ConverterSettings.OutputClassType.IsArray && ConverterSettings.OutputClassType.GetElementType() == ConverterSettings.OutputClassElementType)
1671 if (ConverterSettings.OutputClassElementType == typeof(
string))
1673 var resultList =
new List<string>();
1674 foreach (var dict
in ConverterSettings.InputDictionary)
1676 foreach (var kvp
in dict)
1678 resultList.Add(kvp.Value?.ToString() ??
string.Empty);
1681 ConverterSettings.OutputClass = resultList;
1685 var mi = typeof(ConverterHelper).GetMethod(
"CreateIEnumerableFromClass",
1686 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
1688 new[] { typeof(ConverterSettings) },
1691 var fooRef = mi.MakeGenericMethod(
new[] { ConverterSettings.OutputClassElementType });
1692 ConverterSettings.OutputClass = fooRef.Invoke(
new ConverterHelper(),
new dynamic[] { ConverterSettings });
1699 else if (ConverterSettings.OutputClassType == ConverterSettings.OutputClassType)
1702 if (0 < ConverterSettings.InputDictionary.Count)
1703 ConverterSettings.OutputClass = ConverterHelper.ConvertToClass(classInput, ConverterSettings.OutputClassType, classInput, ConverterSettings.ColumnNames?[0]);
1709 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"Unknown type [{ConverterSettings.OutputClass}].",
this, GPALObjectType.Converter);
1715 if (outputClass != ConverterSettings.OutputClass)
1716 outputClass = (T)ConverterSettings.OutputClass;
1722 else if (ConverterSettings.OutDataFormat == DataFormat.STRING)
1724 outputClass = (T)(
object)ConverterSettings.InputData;
1730 if (
null != ConverterSettings.InputFile)
1732 ((IGPALFileInternal)ConverterSettings.InputFile).FileDictionary.Clear();
1733 ((IGPALFileInternal)ConverterSettings.InputFile).FileData.Clear();
1734 ((IGPALFileInternal)ConverterSettings.InputFile).FileGrid.Clear();
1735 ((IGPALFileInternal)ConverterSettings.InputFile).AlreadyTokenized =
false;
1749 ConverterSettings.RegisteredStructs.Add(knownStructType);
1761 throw new NotImplementedException();
1772 throw new NotImplementedException();
1783 throw new NotImplementedException();
1794 throw new NotImplementedException();
1803 internal static DataFormat GetDataFormat(
char delimiter)
1808 return DataFormat.CARET;
1810 return DataFormat.COLON;
1812 return DataFormat.CSV;
1814 return DataFormat.DOT;
1816 return DataFormat.HYPHEN;
1818 return DataFormat.PIPE;
1820 return DataFormat.SPACE;
1822 return DataFormat.SEMICOLON;
1824 return DataFormat.TAB;
1826 return DataFormat.CUSTOM_DELIMITER;
1837 private List<string> BuildHierarchicalStrings(dynamic dictOrList,
string prefix =
"")
1839 var result =
new List<string>();
1840 if (dictOrList ==
null)
return result;
1843 if (ConverterHelper.IsDictionaryType(dictOrList.GetType()))
1845 foreach (var kvp
in dictOrList)
1847 string key = kvp.Key.ToString();
1848 dynamic value = kvp.Value;
1849 string newPrefix =
string.IsNullOrEmpty(prefix) ?
"" : $
"{prefix}.{key}";
1851 if (ConverterHelper.IsDictionaryType(value.GetType()))
1853 result.AddRange(BuildHierarchicalStrings(value));
1855 else if (ConverterHelper.IsEnumerableType(value.GetType()) && value.GetType() != typeof(
string))
1858 foreach (var item
in value)
1860 string indexedPrefix = $
"{newPrefix}[{index}]";
1861 result.AddRange(BuildHierarchicalStrings(item));
1868 if (
false ==
string.IsNullOrEmpty(newPrefix))
1869 result.Add($
"{newPrefix}={value?.ToString() ?? ""}");
1871 result.Add($
"{value?.ToString() ?? ""}");
1876 else if (ConverterHelper.IsEnumerableType(dictOrList.GetType()) && dictOrList.GetType() != typeof(
string))
1879 foreach (var item
in dictOrList)
1881 string indexedPrefix = $
"{prefix}[{index}]";
1882 result.AddRange(BuildHierarchicalStrings(item, indexedPrefix));
1889 result.Add($
"{prefix}={dictOrList?.ToString() ?? ""}");
1901 private static IEnumerable<object> ConvertToCompatibleItems(
object sourceItem, Type targetType)
1903 var results =
new List<object>();
1905 if (sourceItem ==
null)
1909 if (targetType.IsAssignableFrom(sourceItem.GetType()))
1911 results.Add(sourceItem);
1916 if (sourceItem is IEnumerable enumerable && !(sourceItem is
string))
1918 foreach (var nestedItem
in enumerable)
1920 results.AddRange(ConvertToCompatibleItems(nestedItem, targetType));
1926 if (sourceItem is IDictionary dict)
1928 foreach (var value
in dict.Values)
1930 if (value !=
null && targetType.IsAssignableFrom(value.GetType()))
1934 else if (value !=
null)
1936 results.AddRange(ConvertToCompatibleItems(value, targetType));
1943 var convertedItems =
new List<object>();
1944 var converter = TypeDescriptor.GetConverter(targetType);
1945 if (converter !=
null && converter.CanConvertFrom(sourceItem.GetType()))
1949 var converted = converter.ConvertFrom(sourceItem);
1950 convertedItems.Add(converted);
1957 else if (targetType == typeof(
string))
1961 convertedItems.Add(sourceItem.ToString());
1969 results.AddRange(convertedItems);
1973 internal static class DataFormatDetector
1988 public static DataFormat DetectFormat(
string input)
1990 if (
string.IsNullOrWhiteSpace(input))
1992 GPAL.PublishSimpleEvent(
1993 GPALEventType.WARNING,
1994 "DetectFormat called with empty/null input. Returning NOTSET.",
1995 typeof(DataFormatDetector),
1996 GPALObjectType.Other);
1998 return DataFormat.NOTSET;
2001 string trimmed = input.Trim();
2009 string firstLine = trimmed.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ??
"";
2012 if (trimmed.StartsWith(
"<") && IsLikelyXml(trimmed))
2014 return DataFormat.XML;
2018 if ((trimmed.StartsWith(
"{") || trimmed.StartsWith(
"[")) && IsValidJson(trimmed))
2020 return DataFormat.JSON;
2024 if (!trimmed.StartsWith(
"{") && !trimmed.StartsWith(
"[") && !trimmed.StartsWith(
"<") && IsLikelyYaml(trimmed))
2026 return DataFormat.YAML;
2030 if (trimmed.StartsWith(
"<!DOCTYPE html>") || trimmed.StartsWith(
"<html") || Regex.IsMatch(trimmed,
@"<body|</body|<head|</head", RegexOptions.IgnoreCase))
2032 return DataFormat.HTML;
2036 if (trimmed.StartsWith(
"%PDF-"))
2038 return DataFormat.PDF;
2042 DataFormat delimFormat = DetectDelimiterFormat(trimmed);
2043 if (delimFormat != DataFormat.NOTSET)
2050 if (Regex.IsMatch(firstLine,
@"^\s*CREATE TABLE|SELECT|INSERT|UPDATE", RegexOptions.IgnoreCase))
2052 return DataFormat.DATABASE;
2054 else if (Regex.IsMatch(trimmed,
@"class\s+\w+\s*{|public\s+class", RegexOptions.IgnoreCase))
2056 return DataFormat.CLASS;
2058 else if (IsKeyValueLike(trimmed,
"="))
2060 return DataFormat.DICTIONARY;
2063 GPAL.PublishSimpleEvent(
2064 GPALEventType.DEBUG,
2065 "No clear DataFormat detected for input starting with [" + firstLine.Substring(0, Math.Min(50, firstLine.Length)) +
"]. Returning STRING.",
2067 GPALObjectType.Other);
2069 return DataFormat.STRING;
2079 private static bool IsLikelyBase64(
string s)
2081 if (
string.IsNullOrEmpty(s))
return false;
2082 if (s.Length < 12)
return false;
2084 string alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2087 foreach (
char c
in s)
2089 if (alphabet.IndexOf(c) == -1)
2096 int mod = s.Length % 4;
2100 if (mod != 2 && mod != 0)
2108 int unique =
new HashSet<char>(s).Count;
2109 if (unique < 5 && s.Length > 20)
2121 private static bool IsLikelyXml(
string input)
2125 using (var reader = XmlReader.Create(
new StringReader(input)))
2127 while (reader.Read()) { }
2142 private static bool IsValidJson(
string input)
2146 JsonConvert.DeserializeObject(input);
2161 private static bool IsLikelyYaml(
string input)
2165 var lines = input.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries);
2166 int yamlPatterns = lines.Count(line => Regex.IsMatch(line.Trim(),
@"^[\w\-]+:\s*(.+|$)") || line.StartsWith(
" ") || line.StartsWith(
"- "));
2167 return yamlPatterns > 2 && lines.Length > 1;
2177 private static DataFormat DetectDelimiterFormat(
string input)
2179 var lines = input.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries)
2183 if (lines.Length < 2)
return DataFormat.NOTSET;
2186 var delimiters =
new Dictionary<char, DataFormat>
2188 {
',', DataFormat.CSV },
2189 {
'\t', DataFormat.TAB },
2190 {
'|', DataFormat.PIPE },
2191 {
';', DataFormat.SEMICOLON },
2192 {
' ', DataFormat.SPACE },
2193 {
'^', DataFormat.CARET },
2194 {
'-', DataFormat.HYPHEN },
2195 {
':', DataFormat.COLON },
2196 {
'.', DataFormat.DOT }
2199 foreach (var kvp
in delimiters)
2201 if (IsConsistentDelimiter(lines, kvp.Key))
2203 if (kvp.Key ==
' ')
return DataFormat.PRN;
2209 if (lines.Select(line => line.Split(
new[] {
',',
';',
'\t',
'|',
' ' }, StringSplitOptions.RemoveEmptyEntries).Length).Distinct().Count() == 1)
2211 return DataFormat.CUSTOM_DELIMITER;
2215 if (lines.All(line => line.Length == lines[0].Length) && Regex.IsMatch(lines[0],
@"[a-zA-Z0-9]+\s+[a-zA-Z0-9]+"))
2217 return DataFormat.GRID;
2220 return DataFormat.NOTSET;
2230 private static bool IsConsistentDelimiter(
string[] lines,
char delim)
2232 var colCounts = lines.Select(line => line.Count(c => c == delim) + 1)
2236 return colCounts.Distinct().Count() == 1 && colCounts[0] > 1 && colCounts[0] == colCounts.Max();
2246 private static bool IsKeyValueLike(
string input,
string separator)
2248 var lines = input.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries);
2249 return lines.Count(line => line.Contains(separator) && line.Split(separator.ToCharArray()).Length == 2) > 2;
2260 private string FormatPretty(
string input)
2262 if (
string.IsNullOrWhiteSpace(input))
2265 var format = ConverterSettings.InDataFormat.FirstOrDefault();
2271 case DataFormat.JSON:
2272 var jsonOptions =
new System.Text.Json.JsonSerializerOptions
2274 WriteIndented =
true,
2275 Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
2277 var jsonElement = System.Text.Json.JsonSerializer.Deserialize<System.Text.Json.JsonElement>(input);
2278 return System.Text.Json.JsonSerializer.Serialize(jsonElement, jsonOptions);
2280 case DataFormat.XML:
2281 var xmlDoc = System.Xml.Linq.XDocument.Parse(input, System.Xml.Linq.LoadOptions.PreserveWhitespace);
2282 return xmlDoc.ToString(System.Xml.Linq.SaveOptions.None);
2284 case DataFormat.HTML:
2288 var htmlDoc =
new HtmlAgilityPack.HtmlDocument();
2289 htmlDoc.LoadHtml(input);
2290 return htmlDoc.DocumentNode.WriteContentTo();
2294 var xDoc = System.Xml.Linq.XDocument.Parse(input, System.Xml.Linq.LoadOptions.PreserveWhitespace);
2295 return xDoc.ToString(System.Xml.Linq.SaveOptions.None);
2298 case DataFormat.YAML:
2299 var deserializer =
new DeserializerBuilder().Build();
2300 var serializer =
new SerializerBuilder()
2301 .WithIndentedSequences()
2302 .WithEventEmitter(next =>
new HumanReadableAliasEventEmitter(next))
2304 var obj = deserializer.Deserialize<
object>(input);
2305 return serializer.Serialize(obj);
2308 case DataFormat.CSV:
2309 case DataFormat.TAB:
2310 case DataFormat.LOG:
2311 case DataFormat.PIPE:
2312 case DataFormat.COLON:
2313 case DataFormat.SEMICOLON:
2314 case DataFormat.SPACE:
2315 case DataFormat.HYPHEN:
2316 case DataFormat.CARET:
2317 case DataFormat.PRN:
2318 case DataFormat.BASE64:
2319 case DataFormat.DATABASE:
2320 case DataFormat.XLSX:
2321 case DataFormat.PDF:
2322 case DataFormat.STRING:
2323 case DataFormat.CLASS:
2324 case DataFormat.DICTIONARY:
2325 case DataFormat.DOT:
2326 case DataFormat.GRID:
2333 catch (Exception ex)
2335 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"PrettyPrint failed for [{format}] [{ex.Message}]");