# Converter Factory — GitConverter.Lib This document describes the factory subsystem used to resolve `IConverter` implementations from human-friendly conversion option strings (CLI keys). The factory provides tolerant lookup, helpful suggestions for near-miss keys, and both throwing and non-throwing APIs intended for CLI hosts, programmatic callers and unit tests. Table of contents - Overview - Key concepts - Public API (usage examples) - Registration and aliases - Normalization & suggestion heuristics - Logging and diagnostics - Thread-safety & lifetime - Testing guidance - Extending the factory (adding new converters) - Troubleshooting Overview The `ConverterFactory` implements `IConverterFactory` and maintains a registry mapping display keys (for example "Shapefile" or "GeoJson") to factory delegates (`Func`). Callers can obtain a converter instance either via the non-throwing `TryCreate` method or the throwing `Create` method which raises a `KeyNotFoundException` for unsupported options. Key concepts - Display key: the human-readable label presented to users ("Shapefile"). - Normalized key: compact lookup key produced by `FactoryHelpers.NormalizeKey` (lowercased, stripped of punctuation and quotes) used for tolerant matching. - Factory delegate: `Func` stored in the registry and invoked to produce an `IConverter` instance when requested. - Alias: registration keys may include comma-separated aliases (e.g. "Kml,Kmz") so multiple display names map to the same delegate. Public API (usage examples) Basic, non-throwing usage (recommended for CLI flows): ```csharp IConverterFactory factory = new ConverterFactory(); if (factory.TryCreate("Shapefile", out var converter)) { var result = converter.Convert(inputPath, "Shapefile", "GeoJson", outDir, tempDir); } else { Console.WriteLine("Unsupported option. Supported: " + string.Join(", ", factory.GetSupportedOptions())); } ``` Throwing usage (convenient when callers prefer exceptions for control flow / tests): ```csharp IConverterFactory factory = new ConverterFactory(); try { var converter = factory.Create("GeoJson"); var result = converter.Convert(...); } catch (KeyNotFoundException ex) { Console.WriteLine(ex.Message); // may include a suggestion like "Did you mean 'GeoJson'?" } ``` Registration and aliases The default `ConverterFactory()` constructor registers the library's built-in converters (`EsriJson`, `GeoJson`, `Kml`, `Shapefile`, etc.). For unit testing or hosting scenarios you can construct the factory with a custom registrations map and an optional `IAppLogger`: ```csharp var registrations = new Dictionary> { { "FakeFormat", () => new FakeConverter() }, { "Kml,Kmz", () => new UniversalGisConverter() } }; var factory = new ConverterFactory(registrations, testLogger); ``` Notes about alias keys: - A registration dictionary key may include one or more comma-separated aliases. Each alias is trimmed and registered separately. - Duplicate normalized keys throw an `ArgumentException` during construction to avoid ambiguous resolution. Normalization & suggestion heuristics - Keys are normalized with `FactoryHelpers.NormalizeKey(string)` which trims whitespace and surrounding quotes, converts to lower-case and removes non-alphanumeric characters (ASCII a-z and 0-9). This makes the lookup tolerant to `Esri-Json`, `"GeoJson"` and `geo json`. - When an exact normalized match is not found the factory's `Create` method uses `FactoryHelpers.SuggestClosest` to compute a friendly suggestion (based on Levenshtein edit distance) and includes it in the thrown `KeyNotFoundException` when appropriate. Logging and diagnostics - `ConverterFactory` accepts an optional `IAppLogger` in the constructor. When none is supplied it uses the static `Log.Current` implementation. - Typical log levels and messages: - `Debug`: per-registration messages, lookup misses and instantiation success details. - `Info`: registration summary emitted at factory initialization. - `Warn`: instantiation failures or when an unsupported option is encountered and no suggestion is available. Thread-safety & lifetime - The factory is intended to be constructed once (typically at application startup) and used as a read-only registry thereafter. Registrations are applied only in the constructor and the internal dictionary is not mutated afterwards. - `Create` and `TryCreate` are safe for concurrent calls. If your custom factory delegates use shared resources they must ensure their own thread-safety. Testing guidance - Unit tests should construct a `ConverterFactory` with a small `registrations` dictionary and a test logger to avoid instantiating heavy dependencies. - Use `TryCreate` in tests that assert graceful handling of unknown options. Use `Create` when testing exception paths and suggestion messages. - Example: test suggestion behavior by calling `Create("geojsson")` and assert the thrown exception message contains the suggested key `GeoJson`. Extending the factory (adding new converters) When adding a new converter format: 1. Implement `IConverter` for the new format (or reuse `UniversalGisConverter` where appropriate). 2. Add the registration in `ConverterFactory` constructor (or provide via external registrations): `AddSingle("NewFormat", () => new NewFormatConverter());` 3. Ensure `FileExtensionHelpers` and `ConverterUtils` mappings are updated if the format requires extension or archive requirement logic. 4. Add unit tests for detection, mapping and integration tests with a small sample artifact. Troubleshooting - Duplicate registration errors on factory construction mean two display keys normalize to the same key. Verify alias keys and registration inputs for collisions. - If `TryCreate` returns `false` inspect logs for: - Lookup miss (debug log containing normalized key). - Instantiation failure (warn log with exception message). - If `Create` throws and includes a suggestion, consider displaying the suggestion to the user in help text or documentation. Contact & contribution Open issues for: - Missing converter registrations - Incorrect normalization/suggestion behavior - Test failures related to factory resolution When opening an issue include the attempted option, the normalized key (use `FactoryHelpers.NormalizeKey`) and any logs captured from the calling host.