55
66using Microsoft . Extensions . Logging ;
77using PolylineAlgorithm . Diagnostics ;
8+ using PolylineAlgorithm . Gps . Internal ;
89using PolylineAlgorithm . Internal ;
910using PolylineAlgorithm . Internal . Diagnostics ;
1011using System . Runtime . CompilerServices ;
1112
12- namespace PolylineAlgorithm . Abstraction {
13+ namespace PolylineAlgorithm . Gps . Abstraction {
1314 /// <summary>
1415 /// Decodes encoded polyline strings into sequences of geographic coordinates.
15- /// Implements the <see cref="IPolylineDecoder{TPolyline, TCoordinate }"/> interface.
16+ /// Implements the <see cref="IPolylineDecoder{TPolyline, TValue }"/> interface.
1617 /// </summary>
1718 /// <remarks>
18- /// This abstract class provides a base implementation for decoding polylines, allowing subclasses to define how to handle specific polyline formats.
19+ /// This abstract class provides a base implementation for decoding polylines, allowing subclasses
20+ /// to define how to handle specific polyline input types and how decoded coordinates are represented.
21+ ///
22+ /// The <see cref="Decode(TPolyline, CancellationToken)"/> method uses deferred execution and yields
23+ /// coordinates as they are decoded. Consumers should be aware that decoding occurs during enumeration;
24+ /// any exceptions (format errors, cancellations) are thrown when the returned <see cref="IEnumerable{T}"/>
25+ /// is iterated.
26+ ///
27+ /// The class is safe to use concurrently for decoding different inputs, provided the concrete
28+ /// implementations of <see cref="GetReadOnlyMemory(in TPolyline)"/> and <see cref="CreateCoordinate(double, double)"/>
29+ /// are themselves thread-safe.
1930 /// </remarks>
20- public abstract class AbstractPolylineDecoder < TPolyline , TCoordinate > : IPolylineDecoder < TPolyline , TCoordinate > {
21- private readonly ILogger < AbstractPolylineDecoder < TPolyline , TCoordinate > > _logger ;
31+ public abstract class AbstractPolylineDecoder < TPolyline , TValue > : IPolylineDecoder < TPolyline , TValue > {
32+ private readonly ILogger < AbstractPolylineDecoder < TPolyline , TValue > > _logger ;
2233
2334 /// <summary>
24- /// Initializes a new instance of the <see cref="AbstractPolylineDecoder{TPolyline, TCoordinate }"/> class with default encoding options.
35+ /// Initializes a new instance of the <see cref="AbstractPolylineDecoder{TPolyline, TValue }"/> class with default encoding options.
2536 /// </summary>
2637 protected AbstractPolylineDecoder ( )
2738 : this ( new PolylineEncodingOptions ( ) ) { }
2839
2940 /// <summary>
30- /// Initializes a new instance of the <see cref="AbstractPolylineDecoder{TPolyline, TCoordinate }"/> class with the specified encoding options.
41+ /// Initializes a new instance of the <see cref="AbstractPolylineDecoder{TPolyline, TValue }"/> class with the specified encoding options.
3142 /// </summary>
3243 /// <param name="options">
3344 /// The <see cref="PolylineEncodingOptions"/> to use for encoding operations.
45+ /// This value supplies settings such as numeric precision and a logger factory used to create diagnostic loggers.
3446 /// </param>
3547 /// <exception cref="ArgumentNullException">
36- /// Thrown when <paramref name="options"/> is <see langword="null" />
48+ /// Thrown when <paramref name="options"/> is <see langword="null" />.
3749 /// </exception>
3850 protected AbstractPolylineDecoder ( PolylineEncodingOptions options ) {
3951 if ( options is null ) {
@@ -43,7 +55,7 @@ protected AbstractPolylineDecoder(PolylineEncodingOptions options) {
4355 Options = options ;
4456 _logger = Options
4557 . LoggerFactory
46- . CreateLogger < AbstractPolylineDecoder < TPolyline , TCoordinate > > ( ) ;
58+ . CreateLogger < AbstractPolylineDecoder < TPolyline , TValue > > ( ) ;
4759 }
4860
4961 /// <summary>
@@ -56,27 +68,40 @@ protected AbstractPolylineDecoder(PolylineEncodingOptions options) {
5668 /// </summary>
5769 /// <param name="polyline">The encoded polyline.</param>
5870 /// <param name="cancellationToken">Cancellation token.</param>
59- /// <returns>Decoded coordinates.</returns>
60- /// <exception cref="ArgumentNullException"/>
61- /// <exception cref="ArgumentException"/>
62- /// <exception cref="InvalidPolylineException"/>
63- public IEnumerable < TCoordinate > Decode ( TPolyline polyline , CancellationToken cancellationToken = default ) {
71+ /// <returns>
72+ /// An <see cref="IEnumerable{T}"/> of <typeparamref name="TValue"/> produced by decoding the provided <paramref name="polyline"/>.
73+ /// The enumeration performs the decoding work lazily; exceptions for invalid input or cancellation are raised while enumerating.
74+ /// </returns>
75+ /// <exception cref="ArgumentNullException">
76+ /// Thrown when <paramref name="polyline"/> is <see langword="null"/>.
77+ /// </exception>
78+ /// <exception cref="ArgumentException">
79+ /// Thrown when the polyline contains invalid characters or an otherwise invalid format.
80+ /// </exception>
81+ /// <exception cref="InvalidPolylineException">
82+ /// Thrown when the polyline does not meet minimum structural requirements (for example, too short),
83+ /// or when decoding finds an incomplete coordinate pair.
84+ /// </exception>
85+ /// <exception cref="OperationCanceledException">
86+ /// Thrown when the provided <paramref name="cancellationToken"/> requests cancellation during decoding.
87+ /// </exception>
88+ public IEnumerable < TValue > Decode ( TPolyline polyline , CancellationToken cancellationToken = default ) {
6489 const string OperationName = nameof ( Decode ) ;
6590
66- _logger ? . LogOperationStartedDebug ( OperationName ) ;
91+ try {
92+ _logger ? . LogOperationStartedDebug ( OperationName ) ;
6793
68- ValidateNullPolyline ( polyline , _logger ) ;
94+ ValidateNullPolyline ( polyline , _logger ) ;
6995
70- ReadOnlyMemory < char > sequence = GetReadOnlyMemory ( in polyline ) ;
96+ ReadOnlyMemory < char > sequence = GetReadOnlyMemory ( in polyline ) ;
7197
72- ValidateSequence ( sequence , _logger ) ;
73- ValidateFormat ( sequence , _logger ) ;
98+ ValidateSequence ( sequence , _logger ) ;
99+ ValidateFormat ( sequence , _logger ) ;
74100
75- int position = 0 ;
76- int encodedLatitude = 0 ;
77- int encodedLongitude = 0 ;
101+ int position = 0 ;
102+ int encodedLatitude = 0 ;
103+ int encodedLongitude = 0 ;
78104
79- try {
80105 while ( position < sequence . Length ) {
81106 cancellationToken . ThrowIfCancellationRequested ( ) ;
82107
@@ -130,17 +155,26 @@ private static void ValidateNullPolyline(TPolyline polyline, ILogger? logger) {
130155 /// </exception>
131156 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
132157 private static void ValidateSequence ( ReadOnlyMemory < char > polylineSequence , ILogger ? logger ) {
133- if ( polylineSequence . Length < Defaults . Polyline . Block . Length . Min ) {
158+ const int minLength = 2 ;
159+
160+ if ( polylineSequence . Length < minLength ) {
134161 logger ? . LogOperationFailedDebug ( nameof ( Decode ) ) ;
135- logger ? . LogPolylineCannotBeShorterThanWarning ( polylineSequence . Length , Defaults . Polyline . Block . Length . Min ) ;
162+ logger ? . LogPolylineCannotBeShorterThanWarning ( polylineSequence . Length , 2 ) ;
136163
137- ExceptionGuard . ThrowInvalidPolylineLength ( polylineSequence . Length , Defaults . Polyline . Block . Length . Min ) ;
164+ ExceptionGuard . ThrowInvalidPolylineLength ( polylineSequence . Length , minLength ) ;
138165 }
139166 }
140167
141168 /// <summary>
142- /// Validates the polyline format for allowed characters.
169+ /// Validates the polyline format for allowed characters and structural expectations .
143170 /// </summary>
171+ /// <param name="sequence">The character sequence representing the polyline to validate.</param>
172+ /// <param name="logger">Optional logger used to emit diagnostics when validation fails.</param>
173+ /// <remarks>
174+ /// The default implementation delegates to <see cref="PolylineEncoding.ValidateFormat(ReadOnlySpan{char})"/>.
175+ /// Subclasses MAY override this method to accept alternative encodings or to provide additional validation rules.
176+ /// When validation fails an <see cref="ArgumentException"/> is thrown and a diagnostic warning is logged if a logger is provided.
177+ /// </remarks>
144178 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
145179 protected virtual void ValidateFormat ( ReadOnlyMemory < char > sequence , ILogger ? logger ) {
146180 try {
@@ -152,10 +186,24 @@ protected virtual void ValidateFormat(ReadOnlyMemory<char> sequence, ILogger? lo
152186 }
153187 }
154188
189+ /// <summary>
190+ /// Converts the input <typeparamref name="TPolyline"/> into a <see cref="ReadOnlyMemory{Char}"/> suitable for decoding.
191+ /// </summary>
192+ /// <param name="polyline">The polyline input instance. Implementations receive the value by reference to avoid copies where possible.</param>
193+ /// <returns>
194+ /// A <see cref="ReadOnlyMemory{Char}"/> containing the characters to decode.
195+ /// Implementations should avoid allocating when possible and may return a memory slice of the original input.
196+ /// </returns>
155197 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
156198 protected abstract ReadOnlyMemory < char > GetReadOnlyMemory ( in TPolyline polyline ) ;
157199
200+ /// <summary>
201+ /// Creates a coordinate instance of type <typeparamref name="TValue"/> from decoded latitude and longitude.
202+ /// </summary>
203+ /// <param name="latitude">Decoded latitude value in decimal degrees.</param>
204+ /// <param name="longitude">Decoded longitude value in decimal degrees.</param>
205+ /// <returns>A <typeparamref name="TValue"/> representing the decoded coordinate pair.</returns>
158206 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
159- protected abstract TCoordinate CreateCoordinate ( double latitude , double longitude ) ;
207+ protected abstract TValue CreateCoordinate ( double latitude , double longitude ) ;
160208 }
161209}
0 commit comments