Skip to content

Pr397 - exchange set specification update#406

Open
Cairn23 wants to merge 20 commits into
philliphoff:mainfrom
Cairn23:PR-Exchange-Set-specification-update
Open

Pr397 - exchange set specification update#406
Cairn23 wants to merge 20 commits into
philliphoff:mainfrom
Cairn23:PR-Exchange-Set-specification-update

Conversation

@Cairn23

@Cairn23 Cairn23 commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Spec alignment

Check each spec this PR touches and confirm the relevant skill was
consulted (.github/skills/<spec>/SKILL.md):

  • S-100 framework (s100-framework)
  • S-101 ENC (s101-enc)
  • S-102 bathymetry (s102-bathymetry)
  • S-104 water level (s104-water-level)
  • S-111 surface currents (s111-surface-currents)
  • S-124 navigational warnings (s124-nav-warnings)
  • S-129 under keel clearance (s129-ukc)
  • N/A — change is purely infrastructural (build, CI, docs, tooling)

Spec section references cited in code/docs:

Tests

  • Added/updated xunit tests under tests/
  • Tests requiring real data files use SkippableFact
  • dotnet test --configuration Release passes locally

Documentation

  • Updated the affected project's src/<project>/README.md
  • Updated conceptual docs under docs/ if user-facing behaviour
    changed
  • New public APIs have XML doc comments

Dependencies

  • No new NuGet dependencies, OR versions added to
    Directory.Packages.props (not in the .csproj)
  • gh-advisory-database security check run for any new dependency

Breaking changes

Cairn23 added 3 commits July 1, 2026 08:53
Whilst testing found that a number of items were not geeting the data from the catalogue file dur to the scehme versoins being incorrect and the element tags were not correct

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the exchange set catalogue model and parsing to align more closely with the evolving S-100 Exchange Catalogue schema, and propagates stronger date/enum types into the Viewer’s exchange set header display and related tests.

Changes:

  • Switched multiple catalogue/viewer “date” fields from string to DateOnly? and updated Viewer tests accordingly.
  • Expanded EncDotNet.S100.ExchangeSets catalogue model (new enums/types like Purpose, NavigationPurpose, TemporalExtent, PT_Locale, etc.) and updated ExchangeCatalogueReader to parse additional elements.
  • Updated .gitignore to exclude additional Visual Studio artifacts.

Reviewed changes

Copilot reviewed 19 out of 23 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tests/EncDotNet.S100.Viewer.Tests/ExchangeSetServiceLoaderTests.cs Updates assertions to use DateOnly for exchange set header issue date.
tests/EncDotNet.S100.Viewer.Tests/DatasetsViewModelExchangeSetHeaderTests.cs Updates test calls/assertions for RegisterExchangeSetHeader to use DateOnly.
tests/EncDotNet.S100.Viewer.Tests/DatasetsPanelTabsTests.cs Updates exchange set header registration to pass DateOnly.
tests/EncDotNet.S100.ExchangeSets.Tests/ExchangeCatalogueReaderTests.cs Updates expectations for typed purpose/date and the new DefaultLocale object.
src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs Changes IssueDate to DateOnly? and threads through metadata summary generation.
src/EncDotNet.S100.Viewer/ViewModels/DatasetsViewModel.cs Updates RegisterExchangeSetHeader signature to accept DateOnly?.
src/EncDotNet.S100.Viewer/Services/ExchangeSetService.cs Updates “latest issue date” resolution to work with DateOnly?.
src/EncDotNet.S100.ExchangeSets/TemporalExtent.cs Adds a temporal extent model type for catalogue parsing.
src/EncDotNet.S100.ExchangeSets/Purpose.cs Adds typed purpose enum for dataset discovery metadata.
src/EncDotNet.S100.ExchangeSets/PT_Locale.cs Adds a locale model type used by catalogue/default locale parsing.
src/EncDotNet.S100.ExchangeSets/ProductSpecification.cs Types product specification date and compliancy category.
src/EncDotNet.S100.ExchangeSets/NavigationPurpose.cs Adds typed navigation purpose enum for dataset discovery metadata.
src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs Adds maintenance information model + frequency enum for catalogue parsing.
src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Expands catalogue parsing for new typed fields/locales/coverage/maintenance, plus date/time parsing helpers.
src/EncDotNet.S100.ExchangeSets/ExchangeCatalogue.cs Replaces separate default-locale fields with PT_Locale + other locales list.
src/EncDotNet.S100.ExchangeSets/DatasetDiscoveryMetadata.cs Adds new parsed fields and converts several fields to enums/date/time typed properties.
src/EncDotNet.S100.ExchangeSets/DataCoverage.cs Extends data coverage model with additional fields (scales/resolution/temporal extent).
src/EncDotNet.S100.ExchangeSets/CompliancyCategory.cs Adds compliancy category enum used by product specification parsing.
src/EncDotNet.S100.ExchangeSets/CatalogueDiscoveryMetadata.cs Converts issue date to DateOnly? and replaces locale fields with PT_Locale + other locales.
.gitignore Adds ignores for additional VS solution artifacts.

Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs Outdated
Comment thread src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/PT_Locale.cs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 2, 2026 17:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 12 comments.

Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/DatasetDiscoveryMetadata.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/DatasetDiscoveryMetadata.cs Outdated
Comment thread src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs Outdated
Comment thread src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs
Comment thread src/EncDotNet.S100.Viewer/Services/ExchangeSetService.cs
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 2, 2026 17:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 13 comments.

.Elements(lan + "CountryCode")
.FirstOrDefault();

var country = (string?)langCode?.Attribute("codeListValue");
var charEncode = moreLocal?
.Elements(lan + "characterEncoding")
.FirstOrDefault()?
.Elements(lan + "MD_CharacterSetCode ")
.Elements(lan + "MD_CharacterSetCode ")
.FirstOrDefault();

var encoding = (string?)langCode?.Attribute("codeListValue");
Comment on lines 287 to 294
return new DataCoverage
{
BoundingPolygon = element.Element(xc + "boundingPolygon")?.ToString(),
MaximumDisplayScale = int.TryParse(maxStr, CultureInfo.InvariantCulture, out var max) ? max : null,
MinimumDisplayScale = int.TryParse(minStr, CultureInfo.InvariantCulture, out var min) ? min : null,
OptimumDisplayScale = int.TryParse(optStr, CultureInfo.InvariantCulture, out var opt) ? opt : null,
ApproximateGridResolution = float.TryParse(resStr, CultureInfo.InvariantCulture, out var res) ? res : null,
};
Comment on lines +109 to +118
Purpose? purpose = null;
string? purposeStr = (string?)element.Element(xc + "purpose");
if (purposeStr != null)
purpose = (Purpose)Enum.Parse(typeof(Purpose), purposeStr);


NavigationPurpose? navigationPurpose = null;
string? naxPurposeStr = (string?)element.Element(xc + "navigationPurpose");
if (naxPurposeStr != null)
navigationPurpose = (NavigationPurpose)Enum.Parse(typeof(NavigationPurpose), naxPurposeStr);
if (!string.IsNullOrWhiteSpace(issueDate))
if (issueDate != null)
{
parts.Add(string.Format(Strings.Pane_ExchangeSetHeader_Issued, issueDate));
Comment thread src/EncDotNet.S100.ExchangeSets/PT_Locale.cs Outdated
Comment on lines 267 to 273
[Fact]
public void DefaultLocale_IsNull()
{
var catalogue = ReadTestCatalogue();

Assert.Null(catalogue.DefaultLocaleLanguage);
Assert.Null(catalogue.DefaultLocaleCharacterEncoding);
Assert.Null(catalogue.DefaultLocale);
}
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment on lines +47 to +50
/// <summary>Catalogue-derived issue date string (the latest
/// <c>DatasetDiscoveryMetadata.IssueDate</c> across the set), or
/// <c>null</c> if unknown.</summary>
public string? IssueDate { get; }
public DateOnly? IssueDate { get; }
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 3, 2026 15:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 8 comments.

Comment thread src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs Outdated
Comment on lines +562 to +564
var country = (string?)langCode?.Attribute("codeListValue");
if (country == null)
country = "";
Comment on lines +566 to +574
var charEncode = moreLocal?
.Elements(lan + "characterEncoding")
.FirstOrDefault()?
.Elements(lan + "MD_CharacterSetCode ")
.FirstOrDefault();

var encoding = (string?)langCode?.Attribute("codeListValue");
if (encoding == null)
encoding = "";
Comment on lines 285 to 294
TemporalExtent? temporalExtent = ReadTempoalExtent(element, xc);

return new DataCoverage
{
BoundingPolygon = element.Element(xc + "boundingPolygon")?.ToString(),
MaximumDisplayScale = int.TryParse(maxStr, CultureInfo.InvariantCulture, out var max) ? max : null,
MinimumDisplayScale = int.TryParse(minStr, CultureInfo.InvariantCulture, out var min) ? min : null,
OptimumDisplayScale = int.TryParse(optStr, CultureInfo.InvariantCulture, out var opt) ? opt : null,
ApproximateGridResolution = float.TryParse(resStr, CultureInfo.InvariantCulture, out var res) ? res : null,
};
Comment on lines +92 to +95
CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null)
comp = (CompliancyCategory)Enum.Parse(typeof(CompliancyCategory), compStr);
Comment on lines 703 to 707
/// date strings sort correctly under ordinal comparison, so no
/// parsing is needed for the common case.
/// </summary>
private static string? ResolveLatestIssueDate(
private static DateOnly? ResolveLatestIssueDate(
IReadOnlyList<DatasetDiscoveryMetadata> datasets)
Comment thread src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs Outdated
Comment thread src/EncDotNet.S100.Viewer/ViewModels/ExchangeSetHeader.cs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 6, 2026 08:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 19 comments.

Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs
Comment thread src/EncDotNet.S100.ExchangeSets/Purpose.cs
Comment on lines +1 to +10

namespace EncDotNet.S100.ExchangeSets
{
public enum NavigationPurpose
{
port = 1,
transit = 2,
overview = 3
}
}
Comment thread src/EncDotNet.S100.ExchangeSets/CompliancyCategory.cs
Comment thread src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs Outdated
Comment on lines +496 to +500
private static PT_Locale? ReadPTLocale(XElement? localeElement, XNamespace lan)
{
if (localeElement is null) return null;

XElement? moreLocal = localeElement.Element(lan + "PT_Locale");
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 6, 2026 09:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 8 comments.

Comment on lines 287 to 295
return new DataCoverage
{
BoundingPolygon = element.Element(xc + "boundingPolygon")?.ToString(),
MaximumDisplayScale = int.TryParse(maxStr, CultureInfo.InvariantCulture, out var max) ? max : null,
MinimumDisplayScale = int.TryParse(minStr, CultureInfo.InvariantCulture, out var min) ? min : null,
OptimumDisplayScale = int.TryParse(optStr, CultureInfo.InvariantCulture, out var opt) ? opt : null,
ApproximateGridResolution = float.TryParse(resStr, CultureInfo.InvariantCulture, out var res) ? res : null,
};
}
Comment on lines +562 to +574
var country = (string?)langCode?.Attribute("codeListValue");
if (country == null)
country = "";

var charEncode = moreLocal?
.Elements(lan + "characterEncoding")
.FirstOrDefault()?
.Elements(lan + "MD_CharacterSetCode ")
.FirstOrDefault();

var encoding = (string?)langCode?.Attribute("codeListValue");
if (encoding == null)
encoding = "";
Comment on lines +92 to +95
CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null)
comp = (CompliancyCategory)Enum.Parse(typeof(CompliancyCategory), compStr);
Comment on lines +109 to +118
Purpose? purpose = null;
string? purposeStr = (string?)element.Element(xc + "purpose");
if (purposeStr != null)
purpose = (Purpose)Enum.Parse(typeof(Purpose), purposeStr);


NavigationPurpose? navigationPurpose = null;
string? naxPurposeStr = (string?)element.Element(xc + "navigationPurpose");
if (naxPurposeStr != null)
navigationPurpose = (NavigationPurpose)Enum.Parse(typeof(NavigationPurpose), naxPurposeStr);
DefaultLocale = ReadPTLocale(element.Element(xc + "defaultLocale"), lan),
OtherLocales = element.Elements(xc + "otherLocale").Select(e => ReadPTLocales(e, lan)).ToList(),
MetadataDateStamp = ParseDate((string?)element.Element(xc + "metadataDateStamp")),
ReplaceData = ParseBool(element, "replaceData", xc),
Comment on lines +47 to +50
/// <summary>Catalogue-derived issue date string (the latest
/// <c>DatasetDiscoveryMetadata.IssueDate</c> across the set), or
/// <c>null</c> if unknown.</summary>
public string? IssueDate { get; }
public DateOnly? IssueDate { get; }
Comment on lines +192 to 195
if (issueDate != null)
{
parts.Add(string.Format(Strings.Pane_ExchangeSetHeader_Issued, issueDate));
}
Comment on lines 699 to +706
/// <summary>
/// Returns the lexically-greatest non-null
/// <see cref="DatasetDiscoveryMetadata.IssueDate"/> across the
/// catalogue, or <c>null</c> if no dataset declared one. ISO-8601
/// date strings sort correctly under ordinal comparison, so no
/// parsing is needed for the common case.
/// </summary>
private static string? ResolveLatestIssueDate(
private static DateOnly? ResolveLatestIssueDate(
@Cairn23

Cairn23 commented Jul 6, 2026

Copy link
Copy Markdown
Author

Fixed review comments and merged

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 7 comments.

Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment on lines +109 to +113
Purpose? purpose = null;
string? purposeStr = (string?)element.Element(xc + "purpose");
if (!string.IsNullOrWhiteSpace(purposeStr) && Enum.TryParse<Purpose>(purposeStr, ignoreCase: true, out var parsedPurpose))
purpose = (Purpose)Enum.Parse(typeof(Purpose), purposeStr);

Comment on lines +115 to +118
NavigationPurpose? navigationPurpose = null;
string? naxPurposeStr = (string?)element.Element(xc + "navigationPurpose");
if (naxPurposeStr != null)
navigationPurpose = (NavigationPurpose)Enum.Parse(typeof(NavigationPurpose), naxPurposeStr);
Comment on lines +92 to +95
CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null)
comp = (CompliancyCategory)Enum.Parse(typeof(CompliancyCategory), compStr);
Comment on lines +154 to +156
MetadataDateStamp = ParseDate((string?)element.Element(xc + "metadataDateStamp")),
ReplaceData = ParseBool(element, "replaceData", xc),
NavigationPurpose = navigationPurpose,
Comment on lines +484 to +486
MaintenanceFrequencyCode? maintFreq = null;
if (mfreq != null)
maintFreq = (MaintenanceFrequencyCode)Enum.Parse(typeof(MaintenanceFrequencyCode), mfreq);
Comment on lines 710 to 713
var s = d.IssueDate;
if (string.IsNullOrEmpty(s)) continue;
if (latest is null || string.CompareOrdinal(s, latest) > 0)
if (s == null) continue;
if (latest is null || ((DateOnly)s).CompareTo((DateOnly)latest) > 0)
latest = s;
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 15, 2026 17:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs:95

  • compliancyCategory is parsed via Enum.Parse, which will throw if the XML contains an unexpected value or casing. For a reader that targets real-world catalogues, it’s safer to use Enum.TryParse(..., ignoreCase: true) and leave the value null when unrecognised.
        }

        return root
            .Elements()
            .Where(e => e.Name.LocalName.EndsWith(suffix, StringComparison.Ordinal));

src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs:113

  • The purpose parsing is redundant and can reintroduce case-sensitivity: it first does a case-insensitive Enum.TryParse, but then assigns via Enum.Parse (case-sensitive by default). This can throw on inputs like newDataset if the enum members are later renamed to PascalCase. Prefer assigning the parsedPurpose returned by TryParse.
            City = ReadNestedCharacterString(addressEl, Cit + "city"),
            AdministrativeArea = ReadNestedCharacterString(addressEl, Cit + "administrativeArea"),
            PostalCode = ReadNestedCharacterString(addressEl, Cit + "postalCode"),
            Country = ReadNestedCharacterString(addressEl, Cit + "country"),
        };

Comment on lines +5 to +10
public enum MaintenanceFrequencyCode
{
asNeeded = 1,
irregular = 2
};

Comment on lines +115 to +118
NavigationPurpose? navigationPurpose = null;
string? naxPurposeStr = (string?)element.Element(xc + "navigationPurpose");
if (naxPurposeStr != null)
navigationPurpose = (NavigationPurpose)Enum.Parse(typeof(NavigationPurpose), naxPurposeStr);
Comment on lines +192 to 195
if (issueDate != null)
{
parts.Add(string.Format(Strings.Pane_ExchangeSetHeader_Issued, issueDate));
parts.Add(string.Format(Strings.Pane_ExchangeSetHeader_Issued, issueDate.Value.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture)));
}
Comment on lines +4 to +12
public enum Purpose
{
newDataset = 1,
newEdition = 2,
update = 3,
reissue = 4,
cancellation = 5,
delta = 6,
}
Copilot AI review requested due to automatic review settings July 15, 2026 17:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 10 comments.

Comment thread src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs
Comment on lines +121 to +124
CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null)
comp = (CompliancyCategory)Enum.Parse(typeof(CompliancyCategory), compStr);
Comment on lines +138 to +142
Purpose? purpose = null;
string? purposeStr = (string?)element.Element(xc + "purpose");
if (!string.IsNullOrWhiteSpace(purposeStr) && Enum.TryParse<Purpose>(purposeStr, ignoreCase: true, out var parsedPurpose))
purpose = (Purpose)Enum.Parse(typeof(Purpose), purposeStr);

Comment on lines +144 to +147
NavigationPurpose? navigationPurpose = null;
string? naxPurposeStr = (string?)element.Element(xc + "navigationPurpose");
if (naxPurposeStr != null)
navigationPurpose = (NavigationPurpose)Enum.Parse(typeof(NavigationPurpose), naxPurposeStr);
Comment on lines +513 to +516
MaintenanceFrequencyCode? maintFreq = null;
if (mfreq != null)
maintFreq = (MaintenanceFrequencyCode)Enum.Parse(typeof(MaintenanceFrequencyCode), mfreq);

Comment on lines +538 to +541
var lang = (string?)langCode?.Attribute("codeListValue");
if (lang == null)
lang = "";

Comment on lines +559 to +562
var encoding = (string?)charEncode?.Attribute("codeListValue");
if (encoding == null)
encoding = "";

Comment on lines +636 to +637
// Permit dates are xs:date and may carry a trailing 'Z' (e.g. 2018-03-20Z).
string trimmed = value.Trim().TrimEnd('Z', 'z');
Comment on lines +96 to 100
public string? MetadataPointOfContact { get; init; }
public DateOnly? MetadataDateStamp { get; init; }

public bool? ReplaceData { get; set; }
/// <summary>
Comment on lines 959 to 963
/// <summary>
/// Returns the lexically-greatest non-null
/// <see cref="DatasetDiscoveryMetadata.IssueDate"/> across the
/// catalogue, or <c>null</c> if no dataset declared one. ISO-8601
/// date strings sort correctly under ordinal comparison, so no
/// parsing is needed for the common case.
/// catalogue, or <c>null</c> if no dataset declared one.
/// </summary>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 15, 2026 17:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 6 comments.

Comment on lines +138 to +141
Purpose? purpose = null;
string? purposeStr = (string?)element.Element(xc + "purpose");
if (!string.IsNullOrWhiteSpace(purposeStr) && Enum.TryParse<Purpose>(purposeStr, ignoreCase: true, out var parsedPurpose))
purpose = (Purpose)Enum.Parse(typeof(Purpose), purposeStr);
Comment on lines +116 to +124
CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null)
comp = (CompliancyCategory)Enum.Parse(typeof(CompliancyCategory), compStr);
Comment thread src/EncDotNet.S100.ExchangeSets/ExchangeCatalogueReader.cs Outdated
Comment on lines +188 to +192
}

private static TemporalExtent? ReadTempoalExtent(XElement element, XNamespace xc)
{
var timeInstantEl = element.Element(xc + "temporalExtent");
Comment on lines 959 to 963
/// <summary>
/// Returns the lexically-greatest non-null
/// <see cref="DatasetDiscoveryMetadata.IssueDate"/> across the
/// catalogue, or <c>null</c> if no dataset declared one. ISO-8601
/// date strings sort correctly under ordinal comparison, so no
/// parsing is needed for the common case.
/// catalogue, or <c>null</c> if no dataset declared one.
/// </summary>
Comment on lines +4 to +12
public enum Purpose
{
newDataset = 1,
newEdition = 2,
update = 3,
reissue = 4,
cancellation = 5,
delta = 6,
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 15, 2026 18:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 13 comments.

Comment on lines +138 to +142
Purpose? purpose = null;
string? purposeStr = (string?)element.Element(xc + "purpose");
if (!string.IsNullOrWhiteSpace(purposeStr) && Enum.TryParse<Purpose>(purposeStr, ignoreCase: true, out var parsedPurpose))
purpose = (Purpose)Enum.Parse(typeof(Purpose), purposeStr);

Comment on lines +120 to +124
var numberStr = (string?)element.Element(xc + "number");

CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null)
comp = (CompliancyCategory)Enum.Parse(typeof(CompliancyCategory), compStr);
Comment on lines +513 to +516
MaintenanceFrequencyCode? maintFreq = null;
if (mfreq != null)
maintFreq = (MaintenanceFrequencyCode)Enum.Parse(typeof(MaintenanceFrequencyCode), mfreq);

Comment on lines +549 to +552
var country = (string?)countryCode?.Attribute("codeListValue");
if (country == null)
country = "";

return null;
}

// Permit dates are xs:date and may carry a trailing 'Z' (e.g. 2018-03-20Z).
Comment on lines +4 to +12
public enum Purpose
{
newDataset = 1,
newEdition = 2,
update = 3,
reissue = 4,
cancellation = 5,
delta = 6,
}
Comment on lines +4 to +9
public enum NavigationPurpose
{
port = 1,
transit = 2,
overview = 3
}
Comment on lines +5 to +11
public enum CompliancyCategory
{
category1 = 1,
category2 = 2,
category3 = 3,
category4 = 4,
}
Comment on lines +3 to +7
public enum MaintenanceFrequencyCode
{
asNeeded = 1,
irregular = 2,
}
Comment on lines 148 to +187
@@ -150,25 +159,50 @@
ExpectedHash = ReadExpectedHash(element),
Copyright = ParseBool(element, "copyright", xc),
Classification = ReadCodeListValue(element.Element(xc + "classification")),
Purpose = (string?)element.Element(xc + "purpose"),
Purpose = purpose,
NotForNavigation = ParseBool(element, "notForNavigation", xc),
SpecificUsage = ReadSpecificUsage(element.Element(xc + "specificUsage")),
EditionNumber = ParseInt(element, "editionNumber", xc),
UpdateNumber = ParseInt(element, "updateNumber", xc),
UpdateApplicationDate = (string?)element.Element(xc + "updateApplicationDate"),
IssueDate = (string?)element.Element(xc + "issueDate"),
UpdateApplicationDate = ParseDate((string?)element.Element(xc + "updateApplicationDate")),
ReferenceId = (string?)element.Element(xc + "referenceID"),
IssueDate = ParseDate((string?)element.Element(xc + "issueDate")),
IssueTime = ParseTime((string?)element.Element(xc + "issueTime")),
BoundingBox = ReadBoundingBox(element.Element(xc + "boundingBox")),
TemporalExtent = ReadTempoalExtent(element, xc),
ProductSpecification = ReadProductSpecification(element.Element(xc + "productSpecification"), xc),
ProducingAgency = ReadProducingAgency(element.Element(xc + "producingAgency")),
EncodingFormat = (string?)element.Element(xc + "encodingFormat"),
DataCoverages = element
.Elements(xc + "dataCoverage")
.Select(e => ReadDataCoverage(e, xc))
.ToList(),
DefaultLocaleLanguage = ReadLocaleLanguage(defaultLocaleEl, lan),
DefaultLocaleCharacterEncoding = ReadLocaleCharacterEncoding(defaultLocaleEl, lan),
MetadataDateStamp = (string?)element.Element(xc + "metadataDateStamp"),
NavigationPurpose = (string?)element.Element(xc + "navigationPurpose"),
Comment = (string?)element.Element(xc + "comment"),
DefaultLocale = ReadPTLocale(element.Element(xc + "defaultLocale"), lan),
OtherLocales = element.Elements(xc + "otherLocale").Select(e => ReadPTLocales(e, lan)).ToList(),
MetadataDateStamp = ParseDate((string?)element.Element(xc + "metadataDateStamp")),
ReplaceData = ParseBool(element, "replaceData", xc),
NavigationPurpose = navigationPurpose,
ResourceMaintenance = ReadResourceMaintenance(element.Element(xc + "resourceMaintenance"))
};
Copilot AI review requested due to automatic review settings July 15, 2026 18:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (1)

src/EncDotNet.S100.ExchangeSets/MaintenanceInformation.cs:20

  • This file currently mixes a file-scoped namespace with a block-scoped namespace, has an illegal trailing semicolon after the enum, and is missing the closing brace for the namespace/type block. As written it will not compile.
namespace EncDotNet.S100.ExchangeSets;

namespace EncDotNet.S100.ExchangeSets
{

    public enum MaintenanceFrequencyCode
    {
        AsNeeded = 1,
        Irregular = 2
    };

public sealed class MaintenanceInformation
{
    public MaintenanceFrequencyCode? MaintenanceAndUpdateFrequency { get; init; }

    public DateOnly? MaintenanceDate { get; init; }

    public string? UserDefinedMaintenanceFrequency { get; init; }
}

Comment on lines +121 to +124
CompliancyCategory? comp = null;
string? compStr = (string?)element.Element(xc + "compliancyCategory");
if (compStr != null && Enum.TryParse(typeof(CompliancyCategory), compStr, out var parsedComp))
comp = (CompliancyCategory)parsedComp;
Comment on lines +512 to +515
MaintenanceFrequencyCode? maintFreq = null;
if (mfreq != null)
maintFreq = (MaintenanceFrequencyCode)Enum.Parse(typeof(MaintenanceFrequencyCode), mfreq);

return null;
}

// Permit dates are xs:date and may carry a trailing 'Z' (e.g. 2018-03-20Z).
Comment on lines +537 to +561
var lang = (string?)langCode?.Attribute("codeListValue");
if (lang == null)
lang = "";


var countryCode = moreLocal?
.Elements(lan + "country")
.FirstOrDefault()?
.Elements(lan + "CountryCode")
.FirstOrDefault();

var country = (string?)countryCode?.Attribute("codeListValue");
if (country == null)
country = "";

var charEncode = moreLocal?
.Elements(lan + "characterEncoding")
.FirstOrDefault()?
.Elements(lan + "MD_CharacterSetCode")
.FirstOrDefault();

var encoding = (string?)charEncode?.Attribute("codeListValue");
if (encoding == null)
encoding = "";

Comment on lines +5 to +9
public required string Language { get; init; }

public string? Country { get; init; }

public required string CharacterEncoding { get; init; }
Comment on lines 952 to 956
/// <summary>
/// Returns the lexically-greatest non-null
/// <see cref="DatasetDiscoveryMetadata.IssueDate"/> across the
/// catalogue, or <c>null</c> if no dataset declared one. ISO-8601
/// date strings sort correctly under ordinal comparison, so no
/// parsing is needed for the common case.
/// catalogue, or <c>null</c> if no dataset declared one.
/// </summary>
public string? MetadataPointOfContact { get; init; }
public DateOnly? MetadataDateStamp { get; init; }

public bool? ReplaceData { get; set; }
Comment on lines 153 to 156
public string? DefaultLocaleLanguage { get; init; }

public string? DefaultLocaleCharacterEncoding { get; init; }
public string? DataReplacement { get; init; }

Copilot AI review requested due to automatic review settings July 15, 2026 19:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 23 changed files in this pull request and generated 9 comments.

Comment on lines 952 to 954
/// <summary>
/// Returns the lexically-greatest non-null
/// <see cref="DatasetDiscoveryMetadata.IssueDate"/> across the
/// catalogue, or <c>null</c> if no dataset declared one. ISO-8601
/// date strings sort correctly under ordinal comparison, so no
/// parsing is needed for the common case.
/// </summary>
Comment on lines 961 to 964
var s = d.IssueDate;
if (string.IsNullOrEmpty(s)) continue;
if (latest is null || string.CompareOrdinal(s, latest) > 0)
if (s == null) continue;
if (latest is null || ((DateOnly)s).CompareTo((DateOnly)latest) > 0)
latest = s;
Comment on lines +540 to +543
var lang = (string?)langCode?.Attribute("codeListValue");
if (lang == null)
lang = "";

Comment on lines +551 to +554
var country = (string?)countryCode?.Attribute("codeListValue");
if (country == null)
country = "";

Comment on lines +561 to +564
var encoding = (string?)charEncode?.Attribute("codeListValue");
if (encoding == null)
encoding = "";

Comment on lines +565 to +570
return new PT_Locale
{
Language = lang,
Country = country,
CharacterEncoding =encoding,
};
Comment on lines +179 to +181
Comment = (string?)element.Element(xc + "comment"),
DefaultLocale = ReadPTLocale(element.Element(xc + "defaultLocale"), lan),
OtherLocales = element.Elements(xc + "otherLocale").Select(e => ReadPTLocales(e, lan)).ToList(),
public string? MetadataPointOfContact { get; init; }
public DateOnly? MetadataDateStamp { get; init; }

public bool? ReplaceData { get; init; }
public string? DefaultLocaleLanguage { get; init; }

public string? DefaultLocaleCharacterEncoding { get; init; }
public string? DataReplacement { get; init; }
Copilot AI review requested due to automatic review settings July 15, 2026 19:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 24 changed files in this pull request and generated 9 comments.

Comment on lines 961 to 964
var s = d.IssueDate;
if (string.IsNullOrEmpty(s)) continue;
if (latest is null || string.CompareOrdinal(s, latest) > 0)
if (s == null) continue;
if (latest is null || ((DateOnly)s).CompareTo(latest) > 0)
latest = s;
return null;
}

// Permit times are xs:date and may carry a trailing 'Z' (e.g. 13:45:30Z).
Comment on lines +1 to +10

namespace EncDotNet.S100.ExchangeSets
{
public sealed class TemporalExtent
{
public DateTime? TimeInstantBegin { get; init; }

public DateTime? TimeInstantEnd { get; init; }
}
}
Comment on lines +1 to +13

namespace EncDotNet.S100.ExchangeSets
{
public enum Purpose
{
NewDataset = 1,
NewEdition = 2,
Update = 3,
Reissue = 4,
Cancellation = 5,
Delta = 6,
}
}
Comment on lines +1 to +10

namespace EncDotNet.S100.ExchangeSets
{
public enum NavigationPurpose
{
Port = 1,
Transit = 2,
Overview = 3
}
}
Comment on lines +1 to +19

namespace EncDotNet.S100.ExchangeSets
{

public enum MaintenanceFrequencyCode
{
AsNeeded = 1,
Irregular = 2
}

public sealed class MaintenanceInformation
{
public MaintenanceFrequencyCode? MaintenanceAndUpdateFrequency { get; init; }

public DateOnly? MaintenanceDate { get; init; }

public string? UserDefinedMaintenanceFrequency { get; init; }
}
}
Comment on lines +1 to +12


namespace EncDotNet.S100.ExchangeSets
{
public enum CompliancyCategory
{
Category1 = 1,
Category2 = 2,
Category3 = 3,
Category4 = 4,
}
}
Comment on lines +551 to +554
var country = (string?)countryCode?.Attribute("codeListValue");
if (country == null)
country = "";

Comment on lines 274 to 278
Assert.Equal("DSA", dataset.DigitalSignatureReference);
Assert.Equal("newDataset", dataset.Purpose);
Assert.Equal(Purpose.NewDataset, dataset.Purpose);
Assert.Equal(1, dataset.EditionNumber);
Assert.Equal("2023-01-16", dataset.IssueDate);
Assert.Equal(DateOnly.Parse("2023-01-16"), dataset.IssueDate);
Assert.Equal("ISO/IEC 8211", dataset.EncodingFormat);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants