using Aspose.Gis; using GitConverter.Lib.Logging; using GitConverter.Lib.Models; using System; namespace GitConverter.Lib.Converters { /// /// Universal converter implementation that orchestrates a conversion run using Aspose.GIS drivers. /// /// /// /// UniversalGisConverter is a lightweight orchestrator that performs common conversion /// workflow steps: validate inputs, prepare output/temp folders, extract archives when /// necessary, resolve Aspose drivers and invoke VectorLayer.Convert with format-specific /// options constructed by ConverterUtils.BuildConversionOptions. /// /// /// Logging behavior (messages produced by Convert): /// - Debug: detailed parameter trace including the incoming arguments (path values and /// option strings). Emitted immediately when the conversion starts to help diagnose caller /// behavior and argument repair logic. /// - Info: high-level lifecycle events such as "starting conversion", "Conversion succeeded", /// and conversion attempt boundaries (begin/end). Success messages include the resolved output /// file path. /// - Error: validation errors, archive extraction failures, mapping errors (invalid drivers), /// and unexpected exceptions include error-level logs with exception details where available. /// - Warn: used for non-fatal issues that merit attention (for example when cleanup fails /// or when falling back behavior is used elsewhere in helpers). /// /// /// Callers (including tests) can assert on the presence of key log messages to diagnose failures. /// Typical messages to look for in logs during troubleshooting: /// - "CsvConverter.Convert params:" — parameter tracing (note: the message text currently references /// CsvConverter for historical reasons; it contains the actual parameters passed). /// - "starting conversion (option='...')" — marks the beginning of the conversion attempt. /// - "Conversion succeeded: " — emitted on successful completion and includes output path. /// - "Conversion failed: " — emitted when exceptions occur and includes the exception message. /// - "Cleaning up temp folder." / "Temp folder '' deleted." — cleanup actions after extraction. /// /// public class UniversalGisConverter : IConverter { /// /// Execute a conversion run from a source GIS artifact to a target format. /// /// Path to the GIS input (file or archive). /// Canonical source format option (e.g. "Shapefile"). /// Canonical target format option (e.g. "GeoJson"). /// Destination folder for output files. /// Temporary folder for archive extraction and intermediates. /// A indicating success or a detailed failure. /// /// /// The method logs diagnostic information at multiple levels to aid debugging and test /// assertions. It performs the following high-level steps: /// 1. Validate inputs via . /// 2. Prepare output and temp folders via . /// 3. Prepare the source file (extract archives if necessary) via /// . /// 4. Resolve Aspose drivers using . /// 5. Build an output path and format-specific options. /// 6. Invoke VectorLayer.Convert and return the corresponding . /// /// /// Important logging touch-points (emitted by this method and helpers): /// - Parameter trace at Debug including all incoming arguments. /// - Start/finish markers at Info level surrounding the conversion attempt. /// - Validation and preparation failures logged at Error or Warn depending on severity. /// - Success and failure of the conversion itself logged at Info and Error respectively. /// - Cleanup actions logged at Debug/Info and cleanup failures at Warn or Debug. /// /// /// Note: some log messages currently reference "CsvConverter" in their text due to historical /// naming; the content of those messages still provides accurate parameter and lifecycle data. /// /// public ConversionResult Convert( string gisInputFilePath, string gisSourceFormatOption, string gisTargetFormatOption, string outputFolderPath, string tempFolderPath) { // Parameter trace for diagnostics Log.Debug($"CsvConverter.Convert params: gisInputFilePath='{gisInputFilePath}', gisSourceFormatOption='{gisSourceFormatOption}', gisTargetFormatOption='{gisTargetFormatOption}',outputFolderPath='{outputFolderPath}', tempFolderPath='{tempFolderPath}'"); Log.Info($"CsvConverter: starting conversion (option='{gisTargetFormatOption}', input='{gisInputFilePath}')."); Log.Info($"CsvConverter: starting conversion (option='{gisTargetFormatOption}', input='{gisInputFilePath}')."); // Step 1: Validate inputs var validation = ConverterUtils.ValidateInputs(gisInputFilePath, outputFolderPath, tempFolderPath); if (validation != null) return validation; // Step 2: Prepare directories var preparation = ConverterUtils.PreparePaths(outputFolderPath, tempFolderPath); if (preparation != null) return preparation; bool extractedToTemp = false; try { // Step 3: Handle archive extraction or single file var (sourcePath, wasExtracted, results) = ConverterUtils.PrepareSourceFile( gisInputFilePath, gisSourceFormatOption, tempFolderPath); if (results == null) { return results; } extractedToTemp = wasExtracted; // Step 4: Resolve drivers var srcDriver = ConverterUtils.ConversionOptionToDriver(gisSourceFormatOption) as FileDriver; var destDriver = ConverterUtils.ConversionOptionToDriver(gisTargetFormatOption) as FileDriver; if (srcDriver == null || destDriver == null) { if(srcDriver == null) { Log.Error($"Conversion failed: invalid source format option '{gisSourceFormatOption}'"); } if(destDriver == null) { Log.Error($"Conversion failed: invalid destination format option '{gisTargetFormatOption}'"); } return ConversionResult.Failure("Invalid source or destination format"); } // Step 5: Build output path var outputPath = ConverterUtils.BuildOutputPath(outputFolderPath, gisTargetFormatOption); // Step 6: Build format-specific options var options = ConverterUtils.BuildConversionOptions(gisSourceFormatOption, gisTargetFormatOption); // Step 7: Perform conversion VectorLayer.Convert(sourcePath, srcDriver, outputPath, destDriver, options); Log.Info($"Conversion succeeded: {outputPath}"); return ConversionResult.Success($"Converted to {gisTargetFormatOption}; output: {outputPath}"); } catch (Exception ex) { Log.Error($"Conversion failed: {ex.Message}", ex); return ConversionResult.Failure($"Unexpected error: {ex.Message}"); } finally { Log.Info("Starting conversion attempt."); // Only cleanup when we actually extracted files into the temp folder // This avoids removing a pre-existing temp folder that may be managed by the caller. if (extractedToTemp ) { Log.Debug("Cleaning up temp folder."); try { ConverterUtils.TryCleanupTempFolder(tempFolderPath); } catch (Exception ex) { Log.Debug($"Cleanup failed: {ex.Message}"); } } Log.Info("Finished conversion attempt."); } } } }