From c3e1f1b53ae78697e9a9de66b185c9c8e39ddd87 Mon Sep 17 00:00:00 2001 From: atheate Date: Tue, 2 Jun 2026 15:49:00 +0200 Subject: [PATCH 1/8] [WIP] Validation over parts tree nearly ok, waiting ReferenceUsageExtensions implementation --- .../RuleProcessor.ElementProcessing.cs | 97 +++- .../TEXTUAL_NOTATION_CODEGEN.md | 20 + ...ET.Serializer.TextualNotation.Tests.csproj | 14 + .../01-Parts Tree/1a-Parts Tree.sysmlx | 414 ++++++++++++++++++ .../1c-Parts Tree Redefinition.sysmlx | 363 +++++++++++++++ .../1d-Parts Tree with Reference.sysmlx | 147 +++++++ ...raryRedirectingExternalReferenceService.cs | 160 +++++++ .../TextualNotationValidationTestFixture.cs | 86 ++++ ...FeatureMembershipTextualNotationBuilder.cs | 50 +-- .../MembershipTextualNotationBuilder.cs | 14 +- ...rameterMembershipTextualNotationBuilder.cs | 7 +- .../Writers/NameResolutionCache.cs | 135 +++++- .../NamespaceImportTextualNotationBuilder.cs | 8 +- .../Writers/SharedTextualNotationBuilder.cs | 41 +- SysML2.NET.Serializer.Xmi/DeSerializer.cs | 25 +- 15 files changed, 1504 insertions(+), 77 deletions(-) create mode 100644 SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1a-Parts Tree.sysmlx create mode 100644 SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1c-Parts Tree Redefinition.sysmlx create mode 100644 SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1d-Parts Tree with Reference.sysmlx create mode 100644 SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs create mode 100644 SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs index aa9458a6..b45944f6 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs @@ -396,7 +396,47 @@ internal void ProcessAssignmentElement(EncodedTextWriter writer, IClass umlClass { var previousCaller = ruleGenerationContext.CallerRule; ruleGenerationContext.CallerRule = nonTerminalElement; - ruleGenerationContext.CurrentVariableName = $"poco.{targetPropertyName}"; + + // Thin `[QualifiedName]` wrapper inlining: when the referenced rule's body is + // just `[QualifiedName]` (e.g. FeatureReference, InstantiatedTypeReference) the + // generated `Build{Wrapper}` method receives the target POCO as both target AND + // source for name resolution, which loses the reference site. Inline the + // AppendQualifiedName call here with the OUTER `poco` as the source context so + // imports declared in the source's enclosing namespace can resolve to the + // short / unqualified name. + var referencedRule = ruleGenerationContext.FindRule(nonTerminalElement.Name); + + if (IsThinQualifiedNameWrapperRule(referencedRule)) + { + writer.WriteSafeString($"{Environment.NewLine}if (poco.{targetPropertyName} != null){Environment.NewLine}{{{Environment.NewLine}"); + writer.WriteSafeString($"SharedTextualNotationBuilder.AppendQualifiedName(stringBuilder, poco.{targetPropertyName}, writerContext, poco);{Environment.NewLine}"); + writer.WriteSafeString($"stringBuilder.Append(' ');{Environment.NewLine}"); + writer.WriteSafeString($"}}{Environment.NewLine}"); + ruleGenerationContext.CallerRule = previousCaller; + break; + } + + // Polymorphic `ownedMemberFeature` access on IFeatureMembership: the runtime + // POCO may also be an IParameterMembership (the form used to model operands + // of every InvocationExpression / OperatorExpression per KerML §8.2.5.8.2 + // Notes 1-2 — see Resources/KerML-textual-bnf.kebnf:1176-1178). In that + // shape the operand expression lives under ownedMemberFeature → FeatureValue + // → value rather than directly under ownedMemberFeature. Route the access + // through SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature + // which transparently normalises both runtime shapes into a single feature + // reference downstream code can type-test as before. + if (string.Equals(targetProperty.Name, "ownedMemberFeature", StringComparison.Ordinal) + && QueryIsAssignableToFeatureMembership(umlClass)) + { + const string effectiveVariableName = "effectiveOwnedMemberFeature"; + writer.WriteSafeString($"var {effectiveVariableName} = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco);{Environment.NewLine}"); + ruleGenerationContext.CurrentVariableName = effectiveVariableName; + } + else + { + ruleGenerationContext.CurrentVariableName = $"poco.{targetPropertyName}"; + } + this.ProcessNonTerminalElement(writer, targetProperty.Type as IClass, nonTerminalElement, ruleGenerationContext, isPartOfMultipleAlternative); ruleGenerationContext.CurrentVariableName = "poco"; ruleGenerationContext.CallerRule = previousCaller; @@ -677,5 +717,60 @@ private void EmitSharedNoTargetRuleCall(EncodedTextWriter writer, IClass umlClas writer.WriteSafeString($"{Environment.NewLine}}}"); } } + + /// + /// Returns when IS-A + /// FeatureMembership — used to gate the polymorphic ownedMemberFeature + /// access path that normalises pure IFeatureMembership and + /// IParameterMembership runtime shapes. + /// + /// The under test. + /// if the class IS-A FeatureMembership. + private static bool QueryIsAssignableToFeatureMembership(IClass umlClass) + { + if (umlClass == null) + { + return false; + } + + return string.Equals(umlClass.Name, "FeatureMembership", StringComparison.Ordinal) + || umlClass.QueryAllGeneralClassifiers().Any(c => string.Equals(c.Name, "FeatureMembership", StringComparison.Ordinal)); + } + + /// + /// Returns when is a "thin + /// [QualifiedName] wrapper" — i.e. its body is a single alternative containing a + /// single element that is a [QualifiedName] resolution. Examples in the KerML + /// grammar (Resources/KerML-textual-bnf.kebnf): FeatureReference : Feature = + /// [QualifiedName] (line 1201) and InstantiatedTypeReference : Type = + /// [QualifiedName] (line 1229). + /// + /// When a caller-rule's assignment-element references such a wrapper as its value, the + /// generated Build{Wrapper} method receives the target as both target AND source + /// for name resolution, which loses the syntactic reference site (the caller's + /// poco) needed to honour imports declared in the source's enclosing scope chain. + /// The codegen inlines the AppendQualifiedName call at the caller's emission point + /// instead so the OUTER poco serves as the resolution source. + /// + /// + /// The under test; may be . + /// if the rule is a thin [QualifiedName] wrapper. + private static bool IsThinQualifiedNameWrapperRule(TextualNotationRule rule) + { + if (rule == null || rule.Alternatives.Count != 1) + { + return false; + } + + var alternative = rule.Alternatives[0]; + + if (alternative.Elements.Count != 1) + { + return false; + } + + return alternative.Elements[0] is ValueLiteralElement valueLiteralElement + && valueLiteralElement.QueryIsQualifiedName(); + } } } diff --git a/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md b/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md index 9f34bbb4..c7b5a222 100644 --- a/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md +++ b/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md @@ -447,6 +447,26 @@ When the generator detects an unsupported rule shape it emits a delegating call: Build{RuleName}HandCoded(poco, writerContext, stringBuilder); ``` +### 13.1 Polymorphic `ownedMemberFeature` access on `IFeatureMembership` + +Independent of the per-rule fallback, the generator transparently normalises the two runtime shapes an `IFeatureMembership` can take whenever a rule body accesses the `ownedMemberFeature` scalar property: + +- **Pure `IFeatureMembership` shape** — `membership.ownedMemberFeature` is directly the target feature (typically the rule's expected `IExpression` subtype). This matches the parser-direction production literally (e.g. `SequenceExpressionListMember : FeatureMembership = ownedMemberFeature = SequenceExpressionList`). +- **`IParameterMembership` shape** — used to model the operands of every `InvocationExpression` / `OperatorExpression` per KerML §8.2.5.8.2 Notes 1-2 (`Resources/KerML-textual-bnf.kebnf:1176-1178`, "primary expressions provide additional shorthand notations for certain kinds of InvocationExpressions"). Here `ownedMemberFeature` is the parameter `Feature`; the operand expression lives one level deeper, under that feature's `FeatureValue.value`. + +Because `IParameterMembership : IFeatureMembership`, both shapes pass the rule's `is IFeatureMembership` test but produce structurally different `ownedMemberFeature` trees. The literal codegen produced by `RuleProcessor.ProcessAssignmentElement` would emit `poco.ownedMemberFeature is IExpression …` — a check that silently fails on the parameter-membership shape, dropping the operand. + +To avoid this, `RuleProcessor.ProcessAssignmentElement` (`SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs`) detects the case `targetProperty.Name == "ownedMemberFeature"` AND `umlClass` IS-A `FeatureMembership`, and emits a local-variable declaration at the access point: + +```csharp +var effectiveOwnedMemberFeature = SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); +if (effectiveOwnedMemberFeature is IExpectedExpressionType …) { … } +``` + +`SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature` (`SysML2.NET.Serializer.TextualNotation/Writers/SharedTextualNotationBuilder.cs`) handles the polymorphism: for an `IParameterMembership` it unwraps `ownedMemberFeature.OwnedRelationship.OfType().value`; for the pure shape it returns `ownedMemberFeature` as-is. Downstream type tests in the generated code are unchanged. + +This is a structural fix — not a per-rule allowlist — so every current and future rule whose body accesses `ownedMemberFeature` (`SequenceExpressionListMember`, `OwnedExpressionMember`, `FunctionReferenceMember`, `BodyArgumentMember`, etc.) is corrected uniformly. + The hand-coded partial must: 1. Live in `SysML2.NET.Serializer.TextualNotation/Writers/{ClassName}TextualNotationBuilder.cs` — the file **next to** (not inside) `AutoGenTextualNotationBuilder/`. diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj b/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj index a52cbed3..c8742c0f 100644 --- a/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj +++ b/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj @@ -36,6 +36,8 @@ + + @@ -49,4 +51,16 @@ + + + Always + + + Always + + + Always + + + diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1a-Parts Tree.sysmlx b/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1a-Parts Tree.sysmlx new file mode 100644 index 00000000..4ac4c4a7 --- /dev/null +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1a-Parts Tree.sysmlx @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1c-Parts Tree Redefinition.sysmlx b/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1c-Parts Tree Redefinition.sysmlx new file mode 100644 index 00000000..4e7e596d --- /dev/null +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1c-Parts Tree Redefinition.sysmlx @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1d-Parts Tree with Reference.sysmlx b/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1d-Parts Tree with Reference.sysmlx new file mode 100644 index 00000000..898555b5 --- /dev/null +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Validation/01-Parts Tree/1d-Parts Tree with Reference.sysmlx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs b/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs new file mode 100644 index 00000000..cd98787b --- /dev/null +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs @@ -0,0 +1,160 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright (C) 2022-2026 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace SysML2.NET.Serializer.TextualNotation.Tests.Wrapper +{ + using System; + using System.Collections.Generic; + using System.IO; + + using Microsoft.Extensions.Logging; + + using SysML2.NET.Serializer.Xmi; + + /// + /// Test-only that redirects href references + /// originally generated against a sysml.library/ tree (pilot-implementation layout) + /// to a locally available copy of those library files — typically the + /// SysML2.NET.Serializer.Xmi.Tests/Resources tree mirrored into the test output + /// folder by the test project's csproj. + /// + /// The sysmlx / kermlx files under test are generated by the pilot implementation + /// and emit hrefs of the form ../../../../sysml.library/Domain%20Libraries/.../SI.sysmlx. + /// Those paths do not exist on the test machine. Rather than editing the generated files, + /// this service intercepts each href, detects the sysml.library/ marker segment, and + /// rebuilds the absolute path against , preserving everything + /// after the marker. Hrefs without the marker fall back to the standard relative-to-current + /// resolution used by . + /// + /// + public sealed class LibraryRedirectingExternalReferenceService : IExternalReferenceService + { + /// + /// Marker substring identifying the pilot-implementation library root inside an href. + /// Everything after this segment is treated as a path relative to . + /// + private const string LibraryMarker = "sysml.library/"; + + /// + /// External references queued for processing. + /// + private readonly HashSet pendingReferences = []; + + /// + /// External references already returned via . + /// + private readonly HashSet processedReferences = []; + + /// + /// The local folder that mirrors the pilot-implementation sysml.library tree — + /// typically the test output Resources folder. + /// + private readonly string libraryRoot; + + /// + /// The injected logger used to surface redirect and dedupe events. + /// + private readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Absolute path to the local folder that mirrors the pilot-implementation + /// sysml.library tree (e.g. the test output Resources folder). + /// + /// The injected producing logs. + /// When is . + public LibraryRedirectingExternalReferenceService(string libraryRoot, ILogger logger) + { + this.libraryRoot = libraryRoot ?? throw new ArgumentNullException(nameof(libraryRoot)); + this.logger = logger; + } + + /// + /// Resolves against , + /// redirecting any path containing the sysml.library/ marker to + /// , and queues the resulting absolute for + /// processing. Already-processed references are skipped. + /// + /// The of the file currently being read. + /// The raw href value (without fragment) emitted by the reader. + public void AddExternalReferenceToProcess(Uri currentLocation, string externalReference) + { + var unescaped = Uri.UnescapeDataString(externalReference); + var resolvedUri = this.Redirect(currentLocation, unescaped); + + if (!this.processedReferences.Contains(resolvedUri)) + { + this.pendingReferences.Add(resolvedUri); + } + else + { + this.logger?.LogInformation("File {FileName} already processed", new FileInfo(resolvedUri.LocalPath).Name); + } + } + + /// + /// Returns the queued references and marks them as processed so a subsequent + /// call for the same target is deduped. + /// + /// The references to deserialize next. + public IReadOnlyCollection GetExternalReferencesToProcess() + { + var toBeProcessed = new List(this.pendingReferences); + + foreach (var uri in toBeProcessed) + { + this.processedReferences.Add(uri); + } + + this.pendingReferences.Clear(); + return toBeProcessed; + } + + /// + /// Computes the absolute for an href: when the href contains the + /// sysml.library/ marker, the redirect replaces the marker prefix with + /// ; otherwise the href is resolved against + /// using the standard constructor — + /// matching the production behavior so secondary + /// library-to-library hrefs (already relative to the local libraryRoot layout) resolve naturally. + /// + /// The of the file currently being read. + /// The href value already unescaped via . + /// The absolute to the file to deserialize. + private Uri Redirect(Uri currentLocation, string unescapedHref) + { + var markerIndex = unescapedHref.IndexOf(LibraryMarker, StringComparison.Ordinal); + + if (markerIndex < 0) + { + return new Uri(currentLocation, unescapedHref); + } + + var subPath = unescapedHref.Substring(markerIndex + LibraryMarker.Length); + var redirectedPath = Path.GetFullPath(Path.Combine(this.libraryRoot, subPath)); + + this.logger?.LogInformation("Redirected href '{Href}' to '{Redirected}'", unescapedHref, redirectedPath); + + return new Uri(redirectedPath); + } + } +} diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs b/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs new file mode 100644 index 00000000..d446ba58 --- /dev/null +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs @@ -0,0 +1,86 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright (C) 2022-2026 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace SysML2.NET.Serializer.TextualNotation.Tests.Writers +{ + using System; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + + using Microsoft.Extensions.Logging; + + using NUnit.Framework; + + using SysML2.NET.Serializer.TextualNotation.Tests.Wrapper; + using SysML2.NET.Serializer.TextualNotation.Writers; + using SysML2.NET.Serializer.Xmi; + + [TestFixture] + public class TextualNotationValidationTestFixture + { + [Test] + [TestCase("01-Parts Tree", "1a-Parts Tree.sysmlx")] + [TestCase("01-Parts Tree", "1c-Parts Tree Redefinition.sysmlx")] + [TestCase("01-Parts Tree", "1d-Parts Tree with Reference.sysmlx")] + public async Task VerifyValidationTextualNotationXmi(string folderName, string fileName) + { + var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddConsole(); + builder.SetMinimumLevel(LogLevel.Warning); + }); + + var libraryRoot = Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources"); + + var redirectingService = new LibraryRedirectingExternalReferenceService( + libraryRoot, + loggerFactory.CreateLogger()); + + var deSerializer = new DeSerializer(loggerFactory, redirectingService); + + var filePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Validation", folderName, fileName); + + var rootNamespace = await deSerializer.DeSerializeAsync(new Uri(filePath)); + + using var writerContext = new TextualNotationWriterContext(rootNamespace); + writerContext.EmitOperatorParentheses = false; + var stringBuilder = new StringBuilder(); + + try + { + NamespaceTextualNotationBuilder.BuildRootNamespace(rootNamespace, writerContext, stringBuilder); + } + catch (System.Exception exception) + { + TestContext.WriteLine($"Builder stopped early due to: {exception.Message}, {exception.StackTrace}"); + } + + var textualNotation = stringBuilder.ToString(); + + Assert.That(textualNotation, Is.Not.Empty); + TestContext.WriteLine("=== Textual Notation Output ==="); + TestContext.WriteLine(textualNotation); + TestContext.WriteLine("=== End ==="); + } + } +} diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureMembershipTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureMembershipTextualNotationBuilder.cs index 4cacd4ad..1026926b 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureMembershipTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureMembershipTextualNotationBuilder.cs @@ -468,14 +468,11 @@ public static void BuildTargetTransitionUsageMember(SysML2.NET.Core.POCO.Core.Ty /// The that contains the entire textual notation public static void BuildMetadataBodyUsageMember(SysML2.NET.Core.POCO.Core.Types.IFeatureMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); - if (poco.ownedMemberFeature != null) + if (effectiveOwnedMemberFeature is SysML2.NET.Core.POCO.Systems.DefinitionAndUsage.IReferenceUsage elementAsReferenceUsage) { - - if (poco.ownedMemberFeature is SysML2.NET.Core.POCO.Systems.DefinitionAndUsage.IReferenceUsage elementAsReferenceUsage) - { - ReferenceUsageTextualNotationBuilder.BuildMetadataBodyUsage(elementAsReferenceUsage, writerContext, stringBuilder); - } + ReferenceUsageTextualNotationBuilder.BuildMetadataBodyUsage(elementAsReferenceUsage, writerContext, stringBuilder); } } @@ -551,14 +548,11 @@ public static void BuildOwnedExpressionMember(SysML2.NET.Core.POCO.Core.Types.IF /// The that contains the entire textual notation public static void BuildSequenceExpressionListMember(SysML2.NET.Core.POCO.Core.Types.IFeatureMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); - if (poco.ownedMemberFeature != null) + if (effectiveOwnedMemberFeature is SysML2.NET.Core.POCO.Kernel.Functions.IExpression elementAsExpression) { - - if (poco.ownedMemberFeature is SysML2.NET.Core.POCO.Kernel.Functions.IExpression elementAsExpression) - { - ExpressionTextualNotationBuilder.BuildSequenceExpressionList(elementAsExpression, writerContext, stringBuilder); - } + ExpressionTextualNotationBuilder.BuildSequenceExpressionList(elementAsExpression, writerContext, stringBuilder); } } @@ -572,14 +566,11 @@ public static void BuildSequenceExpressionListMember(SysML2.NET.Core.POCO.Core.T /// The that contains the entire textual notation public static void BuildFunctionReferenceMember(SysML2.NET.Core.POCO.Core.Types.IFeatureMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); - if (poco.ownedMemberFeature != null) + if (effectiveOwnedMemberFeature is SysML2.NET.Core.POCO.Kernel.Functions.IExpression elementAsExpression) { - - if (poco.ownedMemberFeature is SysML2.NET.Core.POCO.Kernel.Functions.IExpression elementAsExpression) - { - ExpressionTextualNotationBuilder.BuildFunctionReference(elementAsExpression, writerContext, stringBuilder); - } + ExpressionTextualNotationBuilder.BuildFunctionReference(elementAsExpression, writerContext, stringBuilder); } } @@ -593,11 +584,8 @@ public static void BuildFunctionReferenceMember(SysML2.NET.Core.POCO.Core.Types. /// The that contains the entire textual notation public static void BuildNamedArgumentMember(SysML2.NET.Core.POCO.Core.Types.IFeatureMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { - - if (poco.ownedMemberFeature != null) - { - FeatureTextualNotationBuilder.BuildNamedArgument(poco.ownedMemberFeature, writerContext, stringBuilder); - } + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); + FeatureTextualNotationBuilder.BuildNamedArgument(effectiveOwnedMemberFeature, writerContext, stringBuilder); } @@ -610,14 +598,11 @@ public static void BuildNamedArgumentMember(SysML2.NET.Core.POCO.Core.Types.IFea /// The that contains the entire textual notation public static void BuildExpressionBodyMember(SysML2.NET.Core.POCO.Core.Types.IFeatureMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); - if (poco.ownedMemberFeature != null) + if (effectiveOwnedMemberFeature is SysML2.NET.Core.POCO.Kernel.Functions.IExpression elementAsExpression) { - - if (poco.ownedMemberFeature is SysML2.NET.Core.POCO.Kernel.Functions.IExpression elementAsExpression) - { - ExpressionTextualNotationBuilder.BuildExpressionBody(elementAsExpression, writerContext, stringBuilder); - } + ExpressionTextualNotationBuilder.BuildExpressionBody(elementAsExpression, writerContext, stringBuilder); } } @@ -655,11 +640,8 @@ public static void BuildPayloadFeatureMember(SysML2.NET.Core.POCO.Core.Types.IFe /// The that contains the entire textual notation public static void BuildMetadataBodyFeatureMember(SysML2.NET.Core.POCO.Core.Types.IFeatureMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { - - if (poco.ownedMemberFeature != null) - { - FeatureTextualNotationBuilder.BuildMetadataBodyFeature(poco.ownedMemberFeature, writerContext, stringBuilder); - } + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); + FeatureTextualNotationBuilder.BuildMetadataBodyFeature(effectiveOwnedMemberFeature, writerContext, stringBuilder); } } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/MembershipTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/MembershipTextualNotationBuilder.cs index 06d506d8..9d807724 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/MembershipTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/MembershipTextualNotationBuilder.cs @@ -136,13 +136,11 @@ public static void BuildFeatureReferenceMember(SysML2.NET.Core.POCO.Root.Namespa if (poco.MemberElement != null) { - - if (poco.MemberElement is SysML2.NET.Core.POCO.Core.Features.IFeature elementAsFeature) - { - FeatureTextualNotationBuilder.BuildFeatureReference(elementAsFeature, writerContext, stringBuilder); - } + SharedTextualNotationBuilder.AppendQualifiedName(stringBuilder, poco.MemberElement, writerContext, poco); + stringBuilder.Append(' '); } + } /// @@ -176,10 +174,12 @@ public static void BuildInstantiatedTypeMember(SysML2.NET.Core.POCO.Root.Namespa if (poco.MemberElement != null) { - if (poco.MemberElement is SysML2.NET.Core.POCO.Core.Types.IType elementAsType) + if (poco.MemberElement != null) { - TypeTextualNotationBuilder.BuildInstantiatedTypeReference(elementAsType, writerContext, stringBuilder); + SharedTextualNotationBuilder.AppendQualifiedName(stringBuilder, poco.MemberElement, writerContext, poco); + stringBuilder.Append(' '); } + } else { diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ParameterMembershipTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ParameterMembershipTextualNotationBuilder.cs index e1bc58f3..d79e8327 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ParameterMembershipTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ParameterMembershipTextualNotationBuilder.cs @@ -300,11 +300,8 @@ public static void BuildMetadataArgumentMember(SysML2.NET.Core.POCO.Kernel.Behav /// The that contains the entire textual notation public static void BuildTypeReferenceMember(SysML2.NET.Core.POCO.Kernel.Behaviors.IParameterMembership poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) { - - if (poco.ownedMemberFeature != null) - { - FeatureTextualNotationBuilder.BuildTypeReference(poco.ownedMemberFeature, writerContext, stringBuilder); - } + var effectiveOwnedMemberFeature = SysML2.NET.Serializer.TextualNotation.Writers.SharedTextualNotationBuilder.QueryEffectiveOwnedMemberFeature(poco); + FeatureTextualNotationBuilder.BuildTypeReference(effectiveOwnedMemberFeature, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs b/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs index 988b1682..7dd7b499 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs @@ -118,9 +118,17 @@ public string Resolve(IElement target, IElement sourcePoco) case null: return string.Empty; - // Short-circuit 1 — IMembership targets: import declarations keep their full path. + // Short-circuit 1 — IMembership targets: import declarations keep the full path, + // but use the SHORTEST declared name available at each owner-chain segment. + // IElement.qualifiedName walks via EscapedName() which prefers `name` over + // `shortName`; that is the inverse of what import declarations need. The SysML + // Textual Notation tutorial and the pilot implementation consistently emit + // imports using shortNames where declared (e.g. `import SI::kg` not + // `import SI::kilogram`). case IMembership membership: - return membership.MemberElement?.qualifiedName ?? string.Empty; + return membership.MemberElement != null + ? QueryShortQualifiedName(membership.MemberElement) + : string.Empty; } // Short-circuit 2 — no usable simple name on the target: emit the qualified name. @@ -169,9 +177,12 @@ private string ResolveFresh(IElement target, IElement sourcePoco, INamespace sou var rawName = target.name; var rawShortName = target.shortName; - var escapedShortName = string.IsNullOrWhiteSpace(rawShortName) - ? null - : (rawShortName.QueryIsValidBasicName() ? rawShortName : rawShortName.ToUnrestrictedName()); + string escapedShortName = null; + + if (!string.IsNullOrWhiteSpace(escapedName)) + { + escapedShortName = rawShortName.QueryIsValidBasicName() ? rawShortName : rawShortName.ToUnrestrictedName(); + } // Walk the cached source-scope chain. First hit wins. foreach (var scope in this.GetSourceScopeChain(sourcePoco, sourceLocalScope)) @@ -285,6 +296,66 @@ private static IReadOnlyList BuildChain(INamespace start) return chain; } + /// + /// Walks up via owningNamespace and builds the + /// qualified name using the SHORTEST declared name at each segment — the + /// when non-blank, otherwise the + /// . Each segment is escaped through the KEBNF + /// unrestricted-name rules ('…' quoting for non-basic identifiers) so the + /// result is parser-roundtrip safe. + /// + /// Used by the short-circuit in for + /// import declarations, where the pilot implementation's reference text uses short + /// forms (e.g. SI::kg) but returns the + /// long form (SI::kilogram) because it goes through EscapedName() which + /// prefers name over shortName. + /// + /// Mirrors the cycle-and-null-safety pattern of : stops + /// on owningNamespace and swallows + /// from unimplemented derived properties. + /// + /// The leaf to qualify; must be non-null. + /// The short-form qualified name (e.g. "SI::kg"), or the empty string + /// when no segment carries a usable name. + private static string QueryShortQualifiedName(IElement element) + { + var segments = new Stack(); + var current = element; + + while (current != null) + { + var preferred = !string.IsNullOrWhiteSpace(current.shortName) + ? current.shortName + : current.name; + + if (string.IsNullOrWhiteSpace(preferred)) + { + break; + } + + var escaped = preferred.QueryIsValidBasicName() + ? preferred + : preferred.ToUnrestrictedName(); + + segments.Push(escaped); + + INamespace next; + + try + { + next = current.owningNamespace; + } + catch (NotSupportedException) + { + break; + } + + current = next; + } + + return string.Join("::", segments); + } + /// /// Resolves the local scope of : the first /// reached by climbing @@ -350,9 +421,15 @@ private INamespace GetSourceLocalScope(IElement sourcePoco) /// /// Tests the simple-name index of for a hit on - /// . Tries the raw name key first, then the raw - /// shortName key. On a hit, receives the + /// . Tries the raw shortName key first, then the raw + /// name key. On a hit, receives the /// corresponding escaped form. + /// + /// The short form is tried first so the emitter prefers it whenever both forms are + /// reachable — the SysML v2 Textual Notation tutorial consistently uses short forms in + /// quantity literals ([kg], [m/s], [s]) and the pilot + /// implementation's reference output matches that convention. + /// /// /// The scope whose index is inspected. /// The element to look up. @@ -366,14 +443,6 @@ private bool TryResolveSimpleNameInScope(INamespace scope, IElement target, stri { var index = this.GetSimpleNameIndex(scope); - if (!string.IsNullOrWhiteSpace(rawName) - && index.TryGetValue(rawName, out var elements) - && elements.Contains(target)) - { - matchedSimpleName = escapedName; - return true; - } - if (!string.IsNullOrWhiteSpace(rawShortName) && index.TryGetValue(rawShortName, out var shortElements) && shortElements.Contains(target)) @@ -382,6 +451,14 @@ private bool TryResolveSimpleNameInScope(INamespace scope, IElement target, stri return true; } + if (!string.IsNullOrWhiteSpace(rawName) + && index.TryGetValue(rawName, out var elements) + && elements.Contains(target)) + { + matchedSimpleName = escapedName; + return true; + } + matchedSimpleName = null; return false; } @@ -563,10 +640,19 @@ private static void BuildInheritedEntries(IType type, Dictionary /// Adds the of - /// to under both its - /// and , and enqueues the target onto - /// if it is itself an (so nested - /// namespaces are indexed too). + /// to under both its short and long name and enqueues the + /// target onto if it is itself an + /// (so nested namespaces are indexed too). + /// + /// Per the metamodel, and + /// are explicit overrides of the member element's + /// declared names within the owning namespace. When the membership does not carry an + /// override (the pilot implementation's XMI never emits memberName / + /// memberShortName on Membership elements), fall back to the target's own + /// / so the simple-name + /// index remains reachable by simple name (e.g. kg, kilogram) for + /// references to imported library elements. + /// /// /// The destination index. /// The membership whose target is indexed. @@ -580,8 +666,15 @@ private static void AddMembershipEntry(Dictionary> ind return; } - AddIndexEntry(index, membership.MemberShortName, target); - AddIndexEntry(index, membership.MemberName, target); + var shortName = !string.IsNullOrWhiteSpace(membership.MemberShortName) + ? membership.MemberShortName + : target.shortName; + var longName = !string.IsNullOrWhiteSpace(membership.MemberName) + ? membership.MemberName + : target.name; + + AddIndexEntry(index, shortName, target); + AddIndexEntry(index, longName, target); if (target is INamespace targetAsNamespace) { diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/NamespaceImportTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/NamespaceImportTextualNotationBuilder.cs index f15d2af4..81d95edc 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/NamespaceImportTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/NamespaceImportTextualNotationBuilder.cs @@ -46,13 +46,15 @@ private static void BuildNamespaceImportHandCoded(INamespaceImport poco, Textual } else if (poco.ImportedNamespace != null) { - stringBuilder.Append(poco.ImportedNamespace.qualifiedName); - stringBuilder.Append("::* "); + SharedTextualNotationBuilder.AppendQualifiedName(stringBuilder, poco.ImportedNamespace, writerContext, poco); + stringBuilder.Append("::*"); if (poco.IsRecursive) { - stringBuilder.Append("::** "); + stringBuilder.Append("::**"); } + + stringBuilder.Append(' '); } } } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/SharedTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/SharedTextualNotationBuilder.cs index 0b061e82..2c1eb432 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/SharedTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/SharedTextualNotationBuilder.cs @@ -177,9 +177,9 @@ private static void BuildNonBehaviorBodyItemHandCoded(IElement poco, TextualNota /// POCO; this method simply emits it as a string. /// /// The that holds the real value expression - /// The used to get access to CursorCollection for the current + /// The used to get access to CursorCollection for the current /// The that contains the entire textual notation - private static void BuildRealValueHandCoded(IExpression poco, TextualNotationWriterContext writerContext, StringBuilder stringBuilder) + private static void BuildRealValueHandCoded(IExpression poco, TextualNotationWriterContext _, StringBuilder stringBuilder) { if (poco is ILiteralRational literalRational) { @@ -639,5 +639,42 @@ internal static void AppendQualifiedName(StringBuilder stringBuilder, IElement t stringBuilder.Append(writerContext.NameResolutionCache.Resolve(target, sourcePoco)); } + + /// + /// Returns the effective ownedMemberFeature of , + /// normalising the two runtime shapes a can take. + /// + /// The pure- shape stores the feature directly: + /// membership.ownedMemberFeature IS the target feature (typically an + /// ) and is returned as-is. The + /// shape — used to model operands of every InvocationExpression / + /// OperatorExpression per KerML §8.2.5.8.2 Notes 1-2 + /// (Resources/KerML-textual-bnf.kebnf:1176-1178) — stores a parameter + /// Feature in ownedMemberFeature and carries the operand expression one + /// level deeper, under that feature's FeatureValue.value. This helper transparently + /// unwraps that extra indirection so every code-generated builder that consumes + /// ownedMemberFeature works uniformly against either shape. + /// + /// Falls back to the direct ownedMemberFeature when the inner unwrap fails + /// (missing or non-feature value), so behaviour is never worse than the literal access. + /// + /// The feature membership; may be . + /// The effective feature, or when none is available. + internal static IFeature QueryEffectiveOwnedMemberFeature(IFeatureMembership membership) + { + var direct = membership?.ownedMemberFeature; + + if (membership is IParameterMembership && direct != null) + { + var featureValue = direct.OwnedRelationship.OfType().FirstOrDefault(); + + if (featureValue?.value is IFeature wrappedExpression) + { + return wrappedExpression; + } + } + + return direct; + } } } diff --git a/SysML2.NET.Serializer.Xmi/DeSerializer.cs b/SysML2.NET.Serializer.Xmi/DeSerializer.cs index 116df4ab..af029412 100644 --- a/SysML2.NET.Serializer.Xmi/DeSerializer.cs +++ b/SysML2.NET.Serializer.Xmi/DeSerializer.cs @@ -60,7 +60,7 @@ public class DeSerializer : IDeSerializer /// /// The injected providing external reference file resolve /// - private readonly ExternalReferenceService externalReferenceService; + private readonly IExternalReferenceService externalReferenceService; /// /// The injected providing resolve feature based on XMI row type @@ -70,13 +70,30 @@ public class DeSerializer : IDeSerializer /// Initializes a new instance of the class. /// The injected used to set up logging public DeSerializer(ILoggerFactory loggerFactory) + : this(loggerFactory, null) + { + } + + /// + /// Initializes a new instance of the class with a caller-supplied + /// . Use this overload to override how + /// href="..." values are resolved against the deserialized file's location + /// (for example to redirect references away from their original on-disk layout). + /// + /// The injected used to set up logging + /// + /// The used to register and resolve external href + /// references. When , a default is created. + /// + public DeSerializer(ILoggerFactory loggerFactory, IExternalReferenceService externalReferenceService) { this.loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; this.logger = this.loggerFactory.CreateLogger(); - - this.externalReferenceService = new ExternalReferenceService(this.loggerFactory.CreateLogger()); + + this.externalReferenceService = externalReferenceService + ?? new ExternalReferenceService(this.loggerFactory.CreateLogger()); this.cache = new XmiDataCache(new PocoReferenceResolveExtensionsFacade(), this.loggerFactory.CreateLogger()); - + this.xmiDataReaderFacade = new XmiDataReaderFacade(); } From d1a477cfe66ec093e7e292b94e78ba7f3aba870d Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 10:16:23 +0200 Subject: [PATCH 2/8] Fix #272: Validation against Parts Tree --- .../HandleBarHelpers/RuleGenerationContext.cs | 10 ++ .../RuleProcessor.CollectionProcessing.cs | 73 +++++++++---- .../HandleBarHelpers/RuleProcessor.cs | 101 ++++++++++++++---- .../TEXTUAL_NOTATION_CODEGEN.md | 2 +- ...AcceptActionUsageTextualNotationBuilder.cs | 2 +- .../ActionUsageTextualNotationBuilder.cs | 10 +- ...rtConstraintUsageTextualNotationBuilder.cs | 2 +- ...gConnectorAsUsageTextualNotationBuilder.cs | 2 +- .../ConnectorTextualNotationBuilder.cs | 2 +- ...ntOccurrenceUsageTextualNotationBuilder.cs | 4 +- ...ExhibitStateUsageTextualNotationBuilder.cs | 2 +- .../FeatureTextualNotationBuilder.cs | 2 +- ...cludeUseCaseUsageTextualNotationBuilder.cs | 2 +- ...erformActionUsageTextualNotationBuilder.cs | 2 +- .../ReferenceUsageTextualNotationBuilder.cs | 2 +- ...yRequirementUsageTextualNotationBuilder.cs | 2 +- .../SendActionUsageTextualNotationBuilder.cs | 4 +- ...SuccessionAsUsageTextualNotationBuilder.cs | 2 +- ...minateActionUsageTextualNotationBuilder.cs | 2 +- .../TransitionUsageTextualNotationBuilder.cs | 4 +- .../UsageTextualNotationBuilder.cs | 2 +- .../ViewUsageTextualNotationBuilder.cs | 2 +- .../Writers/NameResolutionCache.cs | 56 ++++++++-- 23 files changed, 218 insertions(+), 74 deletions(-) diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleGenerationContext.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleGenerationContext.cs index 32303edb..a7836f6c 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleGenerationContext.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleGenerationContext.cs @@ -106,6 +106,16 @@ public TextualNotationRule FindRule(string ruleName) /// public string PendingCursorMove { get; set; } + /// + /// Monotonically-incrementing counter used to produce unique narrowed-type-check pattern + /// variable names (e.g. owningMembership0, owningMembership1) across the + /// emission of a single rule body. Required because C# pattern-matching variables share + /// the enclosing method scope, so emitting more than one + /// is IOwningMembership owningMembership in distinct guards of the same generated + /// method would collide (CS0136). Incremented by the narrowed-type-check emitter. + /// + public int NarrowedTypeCheckCounter { get; set; } + /// /// Determines whether the next sibling element is a terminal that uses AppendLine /// (e.g., {, }, ;), in which case a trailing space would be unnecessary. diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs index 011354d0..c16d5c8d 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs @@ -72,9 +72,9 @@ private void EmitCollectionNonTerminalLoop(EncodedTextWriter writer, IClass umlC cursorVariableName = cursorDefinition.CursorVariableName; } - var perItemCall = this.ResolveBuilderCall(umlClass, nonTerminalElement, typeTarget, ruleGenerationContext); + var perItemCall = ResolveBuilderCall(umlClass, nonTerminalElement, typeTarget, ruleGenerationContext); - var whileTypeExclusion = this.ResolveCollectionWhileTypeCondition(cursorVariableName, umlClass, referencedRule, ruleGenerationContext); + var whileTypeExclusion = ResolveCollectionWhileTypeCondition(cursorVariableName, umlClass, referencedRule, ruleGenerationContext); string whileCondition; @@ -84,23 +84,23 @@ private void EmitCollectionNonTerminalLoop(EncodedTextWriter writer, IClass umlC } else { - var allElements = referencedRule?.Alternatives.SelectMany(alt => alt.Elements).ToList(); + var allElements = referencedRule.Alternatives.SelectMany(alt => alt.Elements).ToList(); - var hasNonAssignmentElements = allElements?.Any(element => + var hasNonAssignmentElements = allElements.Any(element => element is NonTerminalElement or GroupElement) == true; List assignmentTargetTypes = null; if (!hasNonAssignmentElements) { - assignmentTargetTypes = allElements? + assignmentTargetTypes = allElements .OfType() .Where(assignmentElement => assignmentElement.Operator == "+=" && assignmentElement.Value is NonTerminalElement) .Select(assignmentElement => { var valueNonTerminal = (NonTerminalElement)assignmentElement.Value; var refRule = ruleGenerationContext.FindRule(valueNonTerminal.Name); - var targetName = refRule != null ? refRule.EffectiveTarget : null; + var targetName = refRule?.EffectiveTarget; if (targetName != null) { @@ -165,14 +165,14 @@ private void EmitCollectionNonTerminalLoop(EncodedTextWriter writer, IClass umlC var handCodedRuleName = nonTerminalElement.TextualNotationRule?.RuleName ?? nonTerminalElement.Name; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); writer.WriteSafeString(Environment.NewLine); } /// /// Resolves the type condition for a collection while loop. /// - private string ResolveCollectionWhileTypeCondition(string cursorVariableName, IClass umlClass, TextualNotationRule collectionRule, RuleGenerationContext ruleGenerationContext) + private static string ResolveCollectionWhileTypeCondition(string cursorVariableName, IClass umlClass, TextualNotationRule collectionRule, RuleGenerationContext ruleGenerationContext) { var siblings = ruleGenerationContext.CurrentSiblingElements; var currentIndex = ruleGenerationContext.CurrentElementIndex; @@ -347,7 +347,7 @@ private string ResolveContentTypeGuard(string cursorVariableName, TextualNotatio /// /// Resolves the builder method call string for a non-terminal element. /// - private string ResolveBuilderCall(IClass umlClass, NonTerminalElement nonTerminalElement, string typeTarget, RuleGenerationContext ruleGenerationContext) + private static string ResolveBuilderCall(IClass umlClass, NonTerminalElement nonTerminalElement, string typeTarget, RuleGenerationContext ruleGenerationContext) { if (typeTarget == ruleGenerationContext.NamedElementToGenerate.Name) { @@ -386,7 +386,7 @@ private string ResolveBuilderCall(IClass umlClass, NonTerminalElement nonTermina /// The current /// The variable name from which the property is accessed (typically poco) /// The condition expression, or null when no property names are referenced - private string GenerateInlineOptionalCondition(EncodedTextWriter writer, TextualNotationRule referencedRule, IClass targetClass, RuleGenerationContext ruleGenerationContext, string variableName) + private static string GenerateInlineOptionalCondition(EncodedTextWriter writer, TextualNotationRule referencedRule, IClass targetClass, RuleGenerationContext ruleGenerationContext, string variableName) { var propertyNames = referencedRule.QueryAllReferencedPropertyNames(ruleGenerationContext.AllRules); @@ -469,35 +469,70 @@ private static string TryBuildCursorTypedCheck(EncodedTextWriter writer, Textual return null; } - var resolvedTypeNames = new List(); + // Each entry: (WrapperType, InnerType). InnerType is non-null when the assignment's + // referenced rule is a "thin owning wrapper" (target=OwningMembership wrapping a + // single ownedRelatedElement += T); the inner type T then provides the narrowing + // discriminator that distinguishes the wrapper from sibling OwningMembership + // subtypes (e.g. EndFeatureMembership) that share the cursor's collection. + var resolvedTypes = new List<(string Wrapper, string Inner)>(); foreach (var assignmentElement in collectionAssignments) { - var typeName = ResolveAssignmentTargetTypeName(assignmentElement, targetClass, ruleGenerationContext); + var wrapperType = ResolveAssignmentTargetTypeName(assignmentElement, targetClass, ruleGenerationContext); - if (typeName == null) + if (wrapperType == null) { return null; } - if (!resolvedTypeNames.Contains(typeName)) + var innerType = TryResolveWrappedInnerTypeName(assignmentElement, targetClass, ruleGenerationContext); + var entry = (Wrapper: wrapperType, Inner: innerType); + + if (!resolvedTypes.Contains(entry)) { - resolvedTypeNames.Add(typeName); + resolvedTypes.Add(entry); } } var cursorVariableName = EnsureCursorDeclared(writer, property, ruleGenerationContext); - if (resolvedTypeNames.Count == 1) + if (resolvedTypes.Count == 1) { - return $"{cursorVariableName}.Current is {resolvedTypeNames[0]}"; + return BuildTypeCheck(cursorVariableName, resolvedTypes[0].Wrapper, resolvedTypes[0].Inner, ruleGenerationContext); } - var typeChecks = resolvedTypeNames.Select(typeName => $"{cursorVariableName}.Current is {typeName}"); + var typeChecks = resolvedTypes.Select(entry => BuildTypeCheck(cursorVariableName, entry.Wrapper, entry.Inner, ruleGenerationContext)); return $"({string.Join(" || ", typeChecks)})"; } + /// + /// Builds a single cursor-typed boolean check. When is + /// supplied the check narrows from a bare cursor.Current is Wrapper to + /// (cursor.Current is Wrapper owningMembershipN && owningMembershipN.OwnedRelatedElement.OfType<Inner>().Any()) + /// so the discriminator is precise enough to distinguish a thin owning wrapper from + /// sibling subtypes of the same wrapper class. The pattern-variable suffix is drawn from + /// so multiple narrowed + /// checks in the same generated method do not collide on the C# scope (CS0136). + /// + /// The cursor variable name in scope at the emission site. + /// The fully-qualified wrapper type name. + /// The fully-qualified inner element type name when narrowing applies; otherwise . + /// The current providing the per-rule counter for unique pattern-variable names. + /// The C# boolean expression to emit. + private static string BuildTypeCheck(string cursorVariableName, string wrapperType, string innerType, RuleGenerationContext ruleGenerationContext) + { + if (innerType == null) + { + return $"{cursorVariableName}.Current is {wrapperType}"; + } + + var patternVariableName = $"owningMembership{ruleGenerationContext.NarrowedTypeCheckCounter}"; + ruleGenerationContext.NarrowedTypeCheckCounter++; + + return $"({cursorVariableName}.Current is {wrapperType} {patternVariableName} && {patternVariableName}.OwnedRelatedElement.OfType<{innerType}>().Any())"; + } + /// /// Returns the cursor variable name for , reusing an /// already-declared cursor when one is present in @@ -544,7 +579,7 @@ private bool TryEmitOptionalCondition(EncodedTextWriter writer, NonTerminalEleme return false; } - var condition = this.GenerateInlineOptionalCondition(writer, referencedRule, targetClass, ruleGenerationContext, variableName); + var condition = GenerateInlineOptionalCondition(writer, referencedRule, targetClass, ruleGenerationContext, variableName); if (condition == null) { diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs index 94a5d3e5..44f52003 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs @@ -127,7 +127,7 @@ internal void DeclareAllRequiredCursors(EncodedTextWriter writer, IClass umlClas /// When true, suppress duplicate emissions via /// /// - private void EmitHandCodedFallback(EncodedTextWriter writer, string ruleName, RuleGenerationContext ruleGenerationContext, bool deduplicate = false) + private static void EmitHandCodedFallback(EncodedTextWriter writer, string ruleName, RuleGenerationContext ruleGenerationContext, bool deduplicate = false) { if (deduplicate && !ruleGenerationContext.EmittedHandCodedCalls.Add(ruleName)) { @@ -268,6 +268,61 @@ private static string ResolveAssignmentTargetTypeName(AssignmentElement assignme return targetClass?.QueryFullyQualifiedTypeName(); } + /// + /// Resolves the fully-qualified runtime type name of the inner element a "thin owning + /// wrapper" rule wraps. A thin owning wrapper is a rule whose target is + /// OwningMembership and whose body is a single + /// ownedRelatedElement += SomeNonTerminal assignment (e.g. + /// OwnedMultiplicity : OwningMembership = ownedRelatedElement += MultiplicityRange). + /// In such cases the wrapper type (IOwningMembership) is too coarse a discriminator + /// because every OwningMembership subtype (e.g. EndFeatureMembership) also + /// satisfies it; narrowing the check to the wrapped inner element type + /// (IMultiplicityRange) gives the precision the optional-group guard needs. + /// Returns when the assignment's referenced rule does not match + /// the thin-wrapper shape. + /// + /// The += assignment whose referenced rule is inspected. + /// The class hosting the current rule (provides the UML cache). + /// The current . + /// The fully-qualified inner-element type name, or if the rule is not a thin wrapper. + private static string TryResolveWrappedInnerTypeName(AssignmentElement assignmentElement, IClass umlClass, RuleGenerationContext ruleGenerationContext) + { + if (assignmentElement.Value is not NonTerminalElement nonTerminalElement) + { + return null; + } + + var referencedRule = ruleGenerationContext.FindRule(nonTerminalElement.Name); + + if (referencedRule == null + || !string.Equals(referencedRule.EffectiveTarget, "OwningMembership", StringComparison.Ordinal) + || referencedRule.Alternatives.Count != 1) + { + return null; + } + + var alternative = referencedRule.Alternatives[0]; + + if (alternative.Elements.Count != 1 + || alternative.Elements[0] is not AssignmentElement innerAssignment + || !string.Equals(innerAssignment.Property, "ownedRelatedElement", StringComparison.OrdinalIgnoreCase) + || innerAssignment.Value is not NonTerminalElement innerNonTerminal) + { + return null; + } + + var innerRule = ruleGenerationContext.FindRule(innerNonTerminal.Name); + var innerTypeTarget = innerRule?.EffectiveTarget ?? innerNonTerminal.Name; + + if (string.IsNullOrWhiteSpace(innerTypeTarget)) + { + return null; + } + + var innerClass = RuleQueryUtilities.FindClass(umlClass.Cache, innerTypeTarget); + return innerClass?.QueryFullyQualifiedTypeName(); + } + /// /// Processes a single alternative (no branching needed). Handles optional guard emission /// and iterates through the alternative's elements. @@ -372,7 +427,7 @@ private void ProcessSingleAlternative(EncodedTextWriter writer, IClass umlClass, if (referencedRule != null) { - var condition = this.GenerateInlineOptionalCondition(writer, referencedRule, umlClass, ruleGenerationContext, "poco"); + var condition = GenerateInlineOptionalCondition(writer, referencedRule, umlClass, ruleGenerationContext, "poco"); if (condition != null) { @@ -546,7 +601,7 @@ private void ProcessMixedTypeSingleElementAlternatives(EncodedTextWriter writer, { var handCodedRuleName = alternatives.ElementAt(0).TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); } } @@ -568,7 +623,7 @@ private void EmitNonTerminalThenAssignmentDispatch(EncodedTextWriter writer, ICl if (referencedAssignmentNonTerminals.Count != assignmentElements.Count) { var handCodedRuleName = alternatives.ElementAt(0).TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -626,7 +681,7 @@ private void EmitNonTerminalThenAssignmentDispatch(EncodedTextWriter writer, ICl ? nonTerminalReferencedRule.EffectiveTarget : umlClass.Name; - var nonTerminalCall = this.ResolveBuilderCall(umlClass, nonTerminalElement, nonTerminalTypeTarget, ruleGenerationContext); + var nonTerminalCall = ResolveBuilderCall(umlClass, nonTerminalElement, nonTerminalTypeTarget, ruleGenerationContext); if (nonTerminalCall != null) { @@ -658,12 +713,12 @@ private void ProcessMultiElementAlternatives(EncodedTextWriter writer, IClass um // When all alternatives consist exclusively of terminal elements (and optionally non-parsing assignments), handle via code-gen if (alternatives.All(alt => alt.Elements.Count > 0 && alt.Elements.All(element => element is TerminalElement or NonParsingAssignmentElement))) { - this.EmitTerminalOnlyAlternatives(writer, umlClass, alternatives, ruleGenerationContext); + EmitTerminalOnlyAlternatives(writer, umlClass, alternatives, ruleGenerationContext); return; } // Detect pattern: property=[QualifiedName] | property=NonTerminal{containment+=property} - if (alternatives.Count == 2 && this.TryEmitQualifiedNameOrChainAlternatives(writer, umlClass, alternatives, ruleGenerationContext)) + if (alternatives.Count == 2 && TryEmitQualifiedNameOrChainAlternatives(writer, umlClass, alternatives, ruleGenerationContext)) { return; } @@ -701,7 +756,7 @@ private void ProcessMultiElementAlternatives(EncodedTextWriter writer, IClass um var handCodedRuleName = alternatives.ElementAt(0).TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); } } @@ -714,7 +769,7 @@ private void ProcessMultiElementAlternatives(EncodedTextWriter writer, IClass um /// The related /// The grammar alternatives to process /// The current - private void EmitTerminalOnlyAlternatives(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) + private static void EmitTerminalOnlyAlternatives(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) { var nonParsingAssignments = alternatives .SelectMany(alt => alt.Elements.OfType()) @@ -741,10 +796,10 @@ private void EmitTerminalOnlyAlternatives(EncodedTextWriter writer, IClass umlCl writer.WriteSafeString($"switch ({ruleGenerationContext.CurrentVariableName ?? "poco"}.{targetPropertyName}){Environment.NewLine}"); writer.WriteSafeString($"{{{Environment.NewLine}"); - foreach (var alternative in alternatives) + foreach (var alternativeElements in alternatives.Select(x => x.Elements)) { - var nonParsingAssignment = alternative.Elements.OfType().Single(); - var terminals = alternative.Elements.OfType().ToList(); + var nonParsingAssignment = alternativeElements.OfType().Single(); + var terminals = alternativeElements.OfType().ToList(); var enumValueName = nonParsingAssignment.Value.Trim('\'').CapitalizeFirstLetter(); writer.WriteSafeString($"case {targetProperty.Type.QueryFullyQualifiedTypeName()}.{enumValueName}:{Environment.NewLine}"); @@ -781,7 +836,7 @@ private void EmitTerminalOnlyAlternatives(EncodedTextWriter writer, IClass umlCl /// The grammar alternatives to process /// The current /// true if the pattern matched and code was emitted; false otherwise - private bool TryEmitQualifiedNameOrChainAlternatives(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) + private static bool TryEmitQualifiedNameOrChainAlternatives(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) { var qualifiedNameAlt = alternatives.FirstOrDefault(alt => alt.Elements.Count == 1 @@ -905,7 +960,7 @@ private void EmitTerminalVsBodyAlternatives(EncodedTextWriter writer, IClass uml else { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } } } @@ -943,7 +998,7 @@ private void EmitTerminalVsBodyWithCollectionAssignments(EncodedTextWriter write else { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } } @@ -958,7 +1013,7 @@ private void EmitTerminalVsBodyWithCollectionNonTerminals(EncodedTextWriter writ if (nonTerminalRule == null) { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -967,7 +1022,7 @@ private void EmitTerminalVsBodyWithCollectionNonTerminals(EncodedTextWriter writ if (collectionPropertyNames.Count == 0) { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -978,7 +1033,7 @@ private void EmitTerminalVsBodyWithCollectionNonTerminals(EncodedTextWriter writ if (targetProperty == null) { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -1006,7 +1061,7 @@ private void EmitTerminalVsBodyWithCollectionNonTerminals(EncodedTextWriter writ ? referencedRule.EffectiveTarget : umlClass.Name; - var perItemCall = this.ResolveBuilderCall(umlClass, collectionNonTerminal, typeTarget, ruleGenerationContext); + var perItemCall = ResolveBuilderCall(umlClass, collectionNonTerminal, typeTarget, ruleGenerationContext); writer.WriteSafeString($"while ({cursorVarName}.Current != null){Environment.NewLine}"); writer.WriteSafeString($"{{{Environment.NewLine}"); @@ -1047,7 +1102,7 @@ private void EmitTerminalVsBodyWithSingleNonTerminal(EncodedTextWriter writer, I if (nonTerminalRule == null) { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -1056,7 +1111,7 @@ private void EmitTerminalVsBodyWithSingleNonTerminal(EncodedTextWriter writer, I if (collectionPropertyNames.Count == 0) { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -1067,7 +1122,7 @@ private void EmitTerminalVsBodyWithSingleNonTerminal(EncodedTextWriter writer, I if (targetProperty == null) { var handCodedRuleName = firstAlt.TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); return; } @@ -1094,7 +1149,7 @@ private void EmitTerminalVsBodyWithSingleNonTerminal(EncodedTextWriter writer, I ? referencedRule.EffectiveTarget : umlClass.Name; - var perItemCall = this.ResolveBuilderCall(umlClass, singleNonTerminal, typeTarget, ruleGenerationContext); + var perItemCall = ResolveBuilderCall(umlClass, singleNonTerminal, typeTarget, ruleGenerationContext); writer.WriteSafeString($"if ({cursorVarName}.Current != null){Environment.NewLine}"); writer.WriteSafeString($"{{{Environment.NewLine}"); diff --git a/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md b/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md index c7b5a222..be44a645 100644 --- a/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md +++ b/SysML2.NET.CodeGenerator/TEXTUAL_NOTATION_CODEGEN.md @@ -479,7 +479,7 @@ Every hand-coded implementation must be reviewed against the same four invariant 1. **Cursor advancement (§7)** — exactly one `Move()` per `+=` processed; never a `Move()` after a callee that already advanced the cursor. 2. **Quantifier semantics (§12)** — `?` is a single `if`, `*` is a `while` that may execute zero times, `+` is one unconditional emission followed by the same `while`. -3. **Type discriminator correctness (§9)** — the value pattern-matched in a `switch` must be the *actual* element on the cursor (e.g. an `ISpecialization` directly, not the `IOwningMembership` wrapping it). +3. **Type discriminator correctness (§9)** — the value pattern-matched in a `switch` must be the *actual* element on the cursor (e.g. an `ISpecialization` directly, not the `IOwningMembership` wrapping it). The optional-group guard generator now applies this invariant automatically: when an assignment's referenced rule is a "thin owning wrapper" of shape `: OwningMembership = ownedRelatedElement += T`, the emitter narrows the bare `cursor.Current is IOwningMembership` to `(cursor.Current is IOwningMembership om && om.OwnedRelatedElement.OfType().Any())` so a sibling `EndFeatureMembership` (also `IOwningMembership`) cannot spuriously satisfy the guard. The canonical case is `OwnedMultiplicity : OwningMembership = ownedRelatedElement += MultiplicityRange`; the narrowing was added after `BuildBindingConnectorAsUsage` was found emitting a spurious `binding ` prefix because its first ownedRelationship (an `EndFeatureMembership`) was passing `is IOwningMembership`. 4. **Grammar fidelity** — the element order, alternatives, and target metaclass of the hand-coded method must match the KEBNF rule verbatim. Bugs in any of these four areas were the dominant source of regressions during the migration from purely hand-coded builders to the generator-first approach, and they remain the costliest to diagnose because they fail silently rather than throwing. diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AcceptActionUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AcceptActionUsageTextualNotationBuilder.cs index 3a47c88f..3bea6ae7 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AcceptActionUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AcceptActionUsageTextualNotationBuilder.cs @@ -60,7 +60,7 @@ public static void BuildAcceptNodeDeclaration(SysML2.NET.Core.POCO.Systems.Actio { var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { ActionUsageTextualNotationBuilder.BuildActionNodeUsageDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ActionUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ActionUsageTextualNotationBuilder.cs index a72e1d18..87e5b459 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ActionUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ActionUsageTextualNotationBuilder.cs @@ -104,7 +104,7 @@ public static void BuildActionNodeUsageDeclaration(SysML2.NET.Core.POCO.Systems. stringBuilder.Append("action "); var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); } @@ -123,7 +123,7 @@ public static void BuildActionNodePrefix(SysML2.NET.Core.POCO.Systems.Actions.IA OccurrenceUsageTextualNotationBuilder.BuildOccurrenceUsagePrefix(poco, writerContext, stringBuilder); var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { BuildActionNodeUsageDeclaration(poco, writerContext, stringBuilder); } @@ -141,7 +141,7 @@ public static void BuildAssignmentNodeDeclaration(SysML2.NET.Core.POCO.Systems.A { var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { BuildActionNodeUsageDeclaration(poco, writerContext, stringBuilder); stringBuilder.Append(' '); @@ -196,11 +196,11 @@ public static void BuildActionBodyParameter(SysML2.NET.Core.POCO.Systems.Actions { var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { stringBuilder.Append("action "); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership1 && owningMembership1.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AssertConstraintUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AssertConstraintUsageTextualNotationBuilder.cs index a736c77b..9f361192 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AssertConstraintUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/AssertConstraintUsageTextualNotationBuilder.cs @@ -67,7 +67,7 @@ public static void BuildAssertConstraintUsage(SysML2.NET.Core.POCO.Systems.Const } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/BindingConnectorAsUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/BindingConnectorAsUsageTextualNotationBuilder.cs index 559f6b51..b90a5f7c 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/BindingConnectorAsUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/BindingConnectorAsUsageTextualNotationBuilder.cs @@ -46,7 +46,7 @@ public static void BuildBindingConnectorAsUsage(SysML2.NET.Core.POCO.Systems.Con var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); UsageTextualNotationBuilder.BuildUsagePrefix(poco, writerContext, stringBuilder); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { stringBuilder.Append("binding "); UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ConnectorTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ConnectorTextualNotationBuilder.cs index 22e1a092..48997eea 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ConnectorTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ConnectorTextualNotationBuilder.cs @@ -104,7 +104,7 @@ public static void BuildNaryConnectorDeclaration(SysML2.NET.Core.POCO.Kernel.Con var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); var ownedTypeFeaturingCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedTypeFeaturing", poco.ownedTypeFeaturing); - if (poco.IsSufficient || !string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IConjugation || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IDisjoining || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IUnioning || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IIntersecting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IDifferencing || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureChaining || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureInverting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ITypeFeaturing) || poco.IsOrdered || ownedTypeFeaturingCursor.Current is SysML2.NET.Core.POCO.Core.Features.ITypeFeaturing) + if (poco.IsSufficient || !string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any()) || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IConjugation || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IDisjoining || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IUnioning || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IIntersecting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Types.IDifferencing || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureChaining || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureInverting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ITypeFeaturing) || poco.IsOrdered || ownedTypeFeaturingCursor.Current is SysML2.NET.Core.POCO.Core.Features.ITypeFeaturing) { FeatureTextualNotationBuilder.BuildFeatureDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/EventOccurrenceUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/EventOccurrenceUsageTextualNotationBuilder.cs index 1a5cd12a..525a3040 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/EventOccurrenceUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/EventOccurrenceUsageTextualNotationBuilder.cs @@ -84,7 +84,7 @@ public static void BuildEventOccurrenceUsage(SysML2.NET.Core.POCO.Systems.Occurr } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } @@ -93,7 +93,7 @@ public static void BuildEventOccurrenceUsage(SysML2.NET.Core.POCO.Systems.Occurr { stringBuilder.Append("occurrence "); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership1 && owningMembership1.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ExhibitStateUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ExhibitStateUsageTextualNotationBuilder.cs index aa34003f..a9b08d7f 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ExhibitStateUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ExhibitStateUsageTextualNotationBuilder.cs @@ -60,7 +60,7 @@ public static void BuildExhibitStateUsage(SysML2.NET.Core.POCO.Systems.States.IE } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureTextualNotationBuilder.cs index 6faeca91..c7a4f18a 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/FeatureTextualNotationBuilder.cs @@ -1178,7 +1178,7 @@ public static void BuildMetadataBodyFeature(SysML2.NET.Core.POCO.Core.Features.I } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/IncludeUseCaseUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/IncludeUseCaseUsageTextualNotationBuilder.cs index b3d20776..ce9d48a5 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/IncludeUseCaseUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/IncludeUseCaseUsageTextualNotationBuilder.cs @@ -60,7 +60,7 @@ public static void BuildIncludeUseCaseUsage(SysML2.NET.Core.POCO.Systems.UseCase } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/PerformActionUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/PerformActionUsageTextualNotationBuilder.cs index c1aafdb3..13ffbfb7 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/PerformActionUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/PerformActionUsageTextualNotationBuilder.cs @@ -58,7 +58,7 @@ public static void BuildPerformActionUsageDeclaration(SysML2.NET.Core.POCO.Syste } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ReferenceUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ReferenceUsageTextualNotationBuilder.cs index 258d547f..35dce30b 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ReferenceUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ReferenceUsageTextualNotationBuilder.cs @@ -365,7 +365,7 @@ public static void BuildMetadataBodyUsage(SysML2.NET.Core.POCO.Systems.Definitio } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SatisfyRequirementUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SatisfyRequirementUsageTextualNotationBuilder.cs index 9ab5c9a0..9ea997a7 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SatisfyRequirementUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SatisfyRequirementUsageTextualNotationBuilder.cs @@ -67,7 +67,7 @@ public static void BuildSatisfyRequirementUsage(SysML2.NET.Core.POCO.Systems.Req } } - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SendActionUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SendActionUsageTextualNotationBuilder.cs index d21d6f0a..c855fd28 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SendActionUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SendActionUsageTextualNotationBuilder.cs @@ -46,7 +46,7 @@ public static void BuildSendNode(SysML2.NET.Core.POCO.Systems.Actions.ISendActio var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); OccurrenceUsageTextualNotationBuilder.BuildOccurrenceUsagePrefix(poco, writerContext, stringBuilder); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Kernel.FeatureValues.IFeatureValue) || poco.IsOrdered || poco.Direction.HasValue || poco.IsDerived || poco.IsAbstract || poco.IsVariation || poco.IsConstant || poco.IsEnd || poco.isReference || poco.IsIndividual || poco.PortionKind.HasValue || poco.IsComposite || poco.IsPortion || poco.IsVariable || poco.IsSufficient) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any()) || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Kernel.FeatureValues.IFeatureValue) || poco.IsOrdered || poco.Direction.HasValue || poco.IsDerived || poco.IsAbstract || poco.IsVariation || poco.IsConstant || poco.IsEnd || poco.isReference || poco.IsIndividual || poco.PortionKind.HasValue || poco.IsComposite || poco.IsPortion || poco.IsVariable || poco.IsSufficient) { ActionUsageTextualNotationBuilder.BuildActionUsageDeclaration(poco, writerContext, stringBuilder); } @@ -67,7 +67,7 @@ public static void BuildSendNodeDeclaration(SysML2.NET.Core.POCO.Systems.Actions { var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { ActionUsageTextualNotationBuilder.BuildActionNodeUsageDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SuccessionAsUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SuccessionAsUsageTextualNotationBuilder.cs index ac3a8205..be31241f 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SuccessionAsUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/SuccessionAsUsageTextualNotationBuilder.cs @@ -106,7 +106,7 @@ public static void BuildSuccessionAsUsage(SysML2.NET.Core.POCO.Systems.Connectio var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); UsageTextualNotationBuilder.BuildUsagePrefix(poco, writerContext, stringBuilder); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { stringBuilder.Append("succession "); UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TerminateActionUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TerminateActionUsageTextualNotationBuilder.cs index b2d22066..a1b5a151 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TerminateActionUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TerminateActionUsageTextualNotationBuilder.cs @@ -46,7 +46,7 @@ public static void BuildTerminateNode(SysML2.NET.Core.POCO.Systems.Actions.ITerm var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); OccurrenceUsageTextualNotationBuilder.BuildOccurrenceUsagePrefix(poco, writerContext, stringBuilder); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { ActionUsageTextualNotationBuilder.BuildActionNodeUsageDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TransitionUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TransitionUsageTextualNotationBuilder.cs index 4f601269..0a23c5e6 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TransitionUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/TransitionUsageTextualNotationBuilder.cs @@ -106,7 +106,7 @@ public static void BuildGuardedSuccession(SysML2.NET.Core.POCO.Systems.States.IT { var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { stringBuilder.Append("succession "); UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); @@ -202,7 +202,7 @@ public static void BuildTransitionUsage(SysML2.NET.Core.POCO.Systems.States.ITra var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); stringBuilder.Append("transition "); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); stringBuilder.Append("first "); diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/UsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/UsageTextualNotationBuilder.cs index 0f5ba1a2..4166c00e 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/UsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/UsageTextualNotationBuilder.cs @@ -225,7 +225,7 @@ public static void BuildUsageDeclaration(SysML2.NET.Core.POCO.Systems.Definition ElementTextualNotationBuilder.BuildIdentification(poco, writerContext, stringBuilder); var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if ((ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { FeatureTextualNotationBuilder.BuildFeatureSpecializationPart(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ViewUsageTextualNotationBuilder.cs b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ViewUsageTextualNotationBuilder.cs index 1203ff75..41e5d998 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ViewUsageTextualNotationBuilder.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/AutoGenTextualNotationBuilder/ViewUsageTextualNotationBuilder.cs @@ -106,7 +106,7 @@ public static void BuildViewUsage(SysML2.NET.Core.POCO.Systems.Views.IViewUsage stringBuilder.Append("view "); var ownedRelationshipCursor = writerContext.CursorCache.GetOrCreateCursor(poco.Id, "ownedRelationship", poco.OwnedRelationship); - if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership) || poco.IsOrdered) + if (!string.IsNullOrWhiteSpace(poco.DeclaredShortName) || !string.IsNullOrWhiteSpace(poco.DeclaredName) || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IFeatureTyping || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ISubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IReferenceSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.ICrossSubsetting || ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Core.Features.IRedefinition || (ownedRelationshipCursor.Current is SysML2.NET.Core.POCO.Root.Namespaces.IOwningMembership owningMembership0 && owningMembership0.OwnedRelatedElement.OfType().Any())) || poco.IsOrdered) { UsageTextualNotationBuilder.BuildUsageDeclaration(poco, writerContext, stringBuilder); } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs b/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs index 7dd7b499..0d4c6257 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs @@ -179,7 +179,7 @@ private string ResolveFresh(IElement target, IElement sourcePoco, INamespace sou string escapedShortName = null; - if (!string.IsNullOrWhiteSpace(escapedName)) + if (!string.IsNullOrWhiteSpace(rawShortName)) { escapedShortName = rawShortName.QueryIsValidBasicName() ? rawShortName : rawShortName.ToUnrestrictedName(); } @@ -377,13 +377,57 @@ private INamespace GetSourceLocalScope(IElement sourcePoco) while (current != null && visited.Add(current)) { - switch (current) + if (current is IRelationship { OwningRelatedElement: not null } relationship) + { + current = relationship.OwningRelatedElement; + continue; + } + + if (current is INamespace asNamespace) { - case INamespace asNamespace: + // A Namespace is the local scope only when it has a proper upward + // owningNamespace chain. Anonymous nested namespaces (e.g. an + // OwnedFeatureChain Feature owned via Specialization rather than + // Membership) have a null `owningNamespace`; returning such a namespace + // here gives BuildChain a one-element chain that never reaches the + // reference site's enclosing scope, so name resolution falls through + // to qualifiedName. In that case keep walking via `owner` (which follows + // the owningRelationship → OwningRelatedElement path) to find the + // enclosing reference-site namespace. + INamespace asNamespaceUpward = null; + + try + { + asNamespaceUpward = asNamespace.owningNamespace; + } + catch (NotSupportedException) + { + // owningNamespace not implemented — treat as no upward chain. + } + + if (asNamespaceUpward != null || ReferenceEquals(asNamespace, this.RootNamespace)) + { + return asNamespace; + } + + IElement asNamespaceOwner; + + try + { + asNamespaceOwner = asNamespace.owner; + } + catch (NotSupportedException) + { + asNamespaceOwner = null; + } + + if (asNamespaceOwner == null) + { return asNamespace; - case IRelationship { OwningRelatedElement: not null } relationship: - current = relationship.OwningRelatedElement; - continue; + } + + current = asNamespaceOwner; + continue; } INamespace owningNs = null; From b5a8e7ba89d52e81f0ae76ecc3e62f1e11d80057 Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 10:21:49 +0200 Subject: [PATCH 3/8] issues cleanup --- .../RuleProcessor.ElementProcessing.cs | 12 +++--- .../RuleProcessor.PatternHandlers.cs | 38 +++++++++---------- .../HandleBarHelpers/RuleProcessor.cs | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs index b45944f6..3f5d9613 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs @@ -198,13 +198,13 @@ internal void ProcessRuleElement(EncodedTextWriter writer, IClass umlClass, Rule else { var handCodedRuleName = groupElement.TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } } else { var handCodedRuleName = groupElement.TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } } else @@ -237,7 +237,7 @@ internal void ProcessRuleElement(EncodedTextWriter writer, IClass umlClass, Rule else { var handCodedRuleName = textualRuleElement.TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } break; @@ -313,7 +313,7 @@ internal void ProcessAssignmentElement(EncodedTextWriter writer, IClass umlClass else { var handCodedRuleName = assignmentElement.TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } } else @@ -469,7 +469,7 @@ internal void ProcessAssignmentElement(EncodedTextWriter writer, IClass umlClass break; default: var handCodedRuleName = assignmentElement.TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); break; } } @@ -488,7 +488,7 @@ internal void ProcessAssignmentElement(EncodedTextWriter writer, IClass umlClass // Delegate to the HandCoded sibling per the documented convention rather // than emitting a name-collision-prone `Build{Property}(poco, …)` call. var handCodedRuleName = assignmentElement.TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } } diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs index 10d0e11f..3ed6c98a 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs @@ -60,14 +60,14 @@ private bool TryHandleOperatorLiteralAlternation(EncodedTextWriter writer, IClas return false; } - var literals = this.ExtractLiteralAlternation(operatorAssignment.Value, ruleGenerationContext.AllRules); + var literals = ExtractLiteralAlternation(operatorAssignment.Value, ruleGenerationContext.AllRules); if (literals == null || literals.Count == 0) { return false; } - if (!this.AreAlternativeTailElementsProcessable(alternative.Elements, umlClass, ruleGenerationContext)) + if (!AreAlternativeTailElementsProcessable(alternative.Elements, umlClass, ruleGenerationContext)) { return false; } @@ -296,7 +296,7 @@ private bool TryHandlePocoTypeDispatchWithCompoundAlternatives(EncodedTextWriter var orderedBranches = branches .Where(branch => branch.TargetClass != umlClass) - .OrderByDescending(branch => branch.TargetClass.QueryAllGeneralClassifiers().Count()) + .OrderByDescending(branch => branch.TargetClass.QueryAllGeneralClassifiers().Count) .ToList(); writer.WriteSafeString($"switch (poco){Environment.NewLine}"); @@ -306,14 +306,14 @@ private bool TryHandlePocoTypeDispatchWithCompoundAlternatives(EncodedTextWriter { var caseVarName = $"poco{targetClass.Name}"; writer.WriteSafeString($"case {targetClass.QueryFullyQualifiedTypeName()} {caseVarName}:{Environment.NewLine}"); - this.EmitCompoundPocoTypeBranch(writer, umlClass, leadingNonTerminal, alternative, targetClass, caseVarName, ruleGenerationContext); + EmitCompoundPocoTypeBranch(writer, umlClass, leadingNonTerminal, alternative, targetClass, caseVarName); writer.WriteSafeString($"break;{Environment.NewLine}"); } if (defaultBranch.NonTerminal != null) { writer.WriteSafeString($"default:{Environment.NewLine}"); - this.EmitCompoundPocoTypeBranch(writer, umlClass, defaultBranch.NonTerminal, defaultBranch.Alternative, defaultBranch.TargetClass, "poco", ruleGenerationContext); + EmitCompoundPocoTypeBranch(writer, umlClass, defaultBranch.NonTerminal, defaultBranch.Alternative, defaultBranch.TargetClass, "poco"); writer.WriteSafeString($"break;{Environment.NewLine}"); } @@ -428,7 +428,7 @@ private bool TryHandleReferenceOrInline(EncodedTextWriter writer, IClass umlClas /// /// Validates that tail elements of an alternative resolve to existing target classes. /// - private bool AreAlternativeTailElementsProcessable(IReadOnlyList elements, IClass umlClass, RuleGenerationContext ruleGenerationContext) + private static bool AreAlternativeTailElementsProcessable(IReadOnlyList elements, IClass umlClass, RuleGenerationContext ruleGenerationContext) { for (var elementIndex = 1; elementIndex < elements.Count; elementIndex++) { @@ -462,7 +462,7 @@ private bool AreAlternativeTailElementsProcessable(IReadOnlyList el /// /// Extracts literal terminal values from a grammar value element. /// - private List ExtractLiteralAlternation(RuleElement value, IReadOnlyList allRules) + private static List ExtractLiteralAlternation(RuleElement value, IReadOnlyList allRules) { switch (value) { @@ -479,9 +479,9 @@ private List ExtractLiteralAlternation(RuleElement value, IReadOnlyList< var literals = new List(); - foreach (var ruleAlternative in referencedRule.Alternatives) + foreach (var ruleAlternativeElements in referencedRule.Alternatives.Select(x => x.Elements)) { - if (ruleAlternative.Elements.Count != 1 || ruleAlternative.Elements[0] is not TerminalElement nestedTerminal) + if (ruleAlternativeElements.Count != 1 || ruleAlternativeElements[0] is not TerminalElement nestedTerminal) { return null; } @@ -499,10 +499,10 @@ private List ExtractLiteralAlternation(RuleElement value, IReadOnlyList< /// /// Handles alternatives targeting multiple collection properties by falling back to HandCoded. /// - private void ProcessMultiCollectionAssignment(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) + private static void ProcessMultiCollectionAssignment(EncodedTextWriter writer, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) { var handCodedRuleName = alternatives.ElementAt(0).TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext); } /// @@ -535,11 +535,11 @@ private void ProcessUnitypedAlternativesWithOneElement(EncodedTextWriter writer, var elementBoolProps = new List<(NonTerminalElement RuleElement, List BoolProps)>(); - foreach (var element in duplicateGroup.Value) + foreach (var ruleElement in duplicateGroup.Value.Select(x => x.RuleElement)) { - var referencedRule = ruleGenerationContext.AllRules.Single(x => x.RuleName == element.RuleElement.Name); + var referencedRule = ruleGenerationContext.AllRules.Single(x => x.RuleName == ruleElement.Name); var booleanProperties = RuleQueryUtilities.QueryBooleanAssignmentProperties(referencedRule, ruleGenerationContext.AllRules); - elementBoolProps.Add((element.RuleElement, booleanProperties)); + elementBoolProps.Add((ruleElement, booleanProperties)); } for (var elementIndex = 0; elementIndex < elementBoolProps.Count; elementIndex++) @@ -793,8 +793,8 @@ private void ProcessUnitypedAlternativesWithOneElement(EncodedTextWriter writer, return -1; } - var depthA = a.UmlClass.QueryAllGeneralClassifiers().Count(); - var depthB = b.UmlClass.QueryAllGeneralClassifiers().Count(); + var depthA = a.UmlClass.QueryAllGeneralClassifiers().Count; + var depthB = b.UmlClass.QueryAllGeneralClassifiers().Count; return depthB.CompareTo(depthA); }); @@ -821,7 +821,7 @@ private void ProcessUnitypedAlternativesWithOneElement(EncodedTextWriter writer, { var handCodedRuleName = alternatives.ElementAt(0).TextualNotationRule?.RuleName ?? "Unknown"; - this.EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); + EmitHandCodedFallback(writer, handCodedRuleName, ruleGenerationContext, true); break; } @@ -1028,7 +1028,7 @@ private void ProcessUnitypedAlternativesWithOneElement(EncodedTextWriter writer, default: { var defaultHandCodedRuleName = alternatives.ElementAt(0).TextualNotationRule.RuleName; - this.EmitHandCodedFallback(writer, defaultHandCodedRuleName, ruleGenerationContext); + EmitHandCodedFallback(writer, defaultHandCodedRuleName, ruleGenerationContext); break; } } @@ -1037,7 +1037,7 @@ private void ProcessUnitypedAlternativesWithOneElement(EncodedTextWriter writer, /// /// Emits a single type-dispatched branch body. /// - private void EmitCompoundPocoTypeBranch(EncodedTextWriter writer, IClass umlClass, NonTerminalElement leadingNonTerminal, Alternatives alternative, IClass targetClass, string variableName, RuleGenerationContext ruleGenerationContext) + private static void EmitCompoundPocoTypeBranch(EncodedTextWriter writer, IClass umlClass, NonTerminalElement leadingNonTerminal, Alternatives alternative, IClass targetClass, string variableName) { string builderCall; diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs index 44f52003..75128b60 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs @@ -499,7 +499,7 @@ private void ProcessSingleElementAlternatives(EncodedTextWriter writer, IClass u { if (alternatives.ElementAt(0).Elements[0].TextualNotationRule.IsMultiCollectionAssignment) { - this.ProcessMultiCollectionAssignment(writer, umlClass, alternatives, ruleGenerationContext); + ProcessMultiCollectionAssignment(writer, alternatives, ruleGenerationContext); return; } From 42f608047ba6a83ee12548e7b384479626016d4a Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 10:57:46 +0200 Subject: [PATCH 4/8] fix failure on nuget resolve --- Nuget.Config | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Nuget.Config b/Nuget.Config index 03f17c31..425a96fb 100644 --- a/Nuget.Config +++ b/Nuget.Config @@ -1,7 +1,10 @@  - + + + + \ No newline at end of file From f78d4a6cb64da8083ce575ae6183bf99eab32c20 Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 11:17:13 +0200 Subject: [PATCH 5/8] test with gh actions env --- .github/workflows/CodeQuality.yml | 9 +++++++++ .github/workflows/codeql-analysis.yml | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/.github/workflows/CodeQuality.yml b/.github/workflows/CodeQuality.yml index 9cbf3532..3b922640 100644 --- a/.github/workflows/CodeQuality.yml +++ b/.github/workflows/CodeQuality.yml @@ -13,6 +13,15 @@ jobs: build: name: Build runs-on: ubuntu-latest + env: + # Use cached CRL data only for cert revocation checks. Works around + # NU3012 ("certificate revoked") for transitive ReactiveUI / Splat 19.3.1 + # packages whose author certificate (Glenn Watson) was revoked at the CA + # in 2026-Q2. `signatureValidationMode=accept` in NuGet.Config does NOT + # cover this case because it only relaxes the "require signature" check, + # not the chain-validation step that NU3012 fires from. Revert this env + # var once the affected packages are re-signed and re-published. + NUGET_CERT_REVOCATION_MODE: offline steps: - uses: actions/checkout@v6.0.2 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 65ec8ac0..6df417a6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -11,6 +11,13 @@ jobs: runs-on: ubuntu-latest + env: + # Use cached CRL data only for cert revocation checks. Works around + # NU3012 ("certificate revoked") for transitive ReactiveUI / Splat 19.3.1 + # packages whose author certificate was revoked at the CA in 2026-Q2. + # Revert this env var once the affected packages are re-signed. + NUGET_CERT_REVOCATION_MODE: offline + steps: - name: Checkout repository uses: actions/checkout@v6.0.2 From b595a5e4f7dba4f47291c48ae5f66555139c4b68 Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 11:36:35 +0200 Subject: [PATCH 6/8] SQ fix --- .../RuleProcessor.CollectionProcessing.cs | 4 ++-- .../RuleProcessor.ElementProcessing.cs | 6 +++--- .../HandleBarHelpers/RuleProcessor.PatternHandlers.cs | 2 +- .../HandleBarHelpers/RuleProcessor.cs | 2 +- .../LibraryRedirectingExternalReferenceService.cs | 10 ++++++++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs index c16d5c8d..8a7b68b0 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.CollectionProcessing.cs @@ -87,7 +87,7 @@ private void EmitCollectionNonTerminalLoop(EncodedTextWriter writer, IClass umlC var allElements = referencedRule.Alternatives.SelectMany(alt => alt.Elements).ToList(); var hasNonAssignmentElements = allElements.Any(element => - element is NonTerminalElement or GroupElement) == true; + element is NonTerminalElement or GroupElement); List assignmentTargetTypes = null; @@ -567,7 +567,7 @@ private static string EnsureCursorDeclared(EncodedTextWriter writer, IProperty p /// /// Emits an optional condition wrapping block for an optional NonTerminal element. /// - private bool TryEmitOptionalCondition(EncodedTextWriter writer, NonTerminalElement nonTerminalElement, TextualNotationRule referencedRule, IClass targetClass, RuleGenerationContext ruleGenerationContext, string variableName) + private static bool TryEmitOptionalCondition(EncodedTextWriter writer, NonTerminalElement nonTerminalElement, TextualNotationRule referencedRule, IClass targetClass, RuleGenerationContext ruleGenerationContext, string variableName) { if (!nonTerminalElement.IsOptional || nonTerminalElement.IsCollection) { diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs index 3f5d9613..bf0692c1 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs @@ -561,7 +561,7 @@ internal void ProcessNonTerminalElement(EncodedTextWriter writer, IClass umlClas writer.WriteSafeString($"{{{Environment.NewLine}"); } - var emittedCondition = this.TryEmitOptionalCondition(writer, nonTerminalElement, referencedRule, targetClass, ruleGenerationContext, ruleGenerationContext.CurrentVariableName); + var emittedCondition = TryEmitOptionalCondition(writer, nonTerminalElement, referencedRule, targetClass, ruleGenerationContext, ruleGenerationContext.CurrentVariableName); writer.WriteSafeString($"{targetType.Name}TextualNotationBuilder.Build{nonTerminalElement.Name}({ruleGenerationContext.CurrentVariableName}, writerContext, stringBuilder);"); @@ -614,7 +614,7 @@ internal void ProcessNonTerminalElement(EncodedTextWriter writer, IClass umlClas { var variableToUse = referencedRule != null ? ruleGenerationContext.CurrentVariableName : "poco"; - var emittedSameClassCondition = this.TryEmitOptionalCondition(writer, nonTerminalElement, referencedRule, umlClass, ruleGenerationContext, variableToUse); + var emittedSameClassCondition = TryEmitOptionalCondition(writer, nonTerminalElement, referencedRule, umlClass, ruleGenerationContext, variableToUse); writer.WriteSafeString($"Build{nonTerminalElement.Name}({variableToUse}, writerContext, stringBuilder);"); @@ -708,7 +708,7 @@ private void EmitSharedNoTargetRuleCall(EncodedTextWriter writer, IClass umlClas } var emittedCondition = effectiveTarget != null - && this.TryEmitOptionalCondition(writer, nonTerminalElement, referencedRule, effectiveTarget, ruleGenerationContext, ruleGenerationContext.CurrentVariableName); + && TryEmitOptionalCondition(writer, nonTerminalElement, referencedRule, effectiveTarget, ruleGenerationContext, ruleGenerationContext.CurrentVariableName); writer.WriteSafeString($"{RulesHelper.SharedBuilderClassName}.Build{nonTerminalElement.Name}({variableExpression}, writerContext, stringBuilder);"); diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs index 3ed6c98a..8bbb6f10 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.PatternHandlers.cs @@ -237,7 +237,7 @@ private bool TryHandleEmptyVsNonEmptyMembership(EncodedTextWriter writer, IClass /// /// Pattern C: detects poco-runtime-type dispatch with compound alternatives. /// - private bool TryHandlePocoTypeDispatchWithCompoundAlternatives(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) + private static bool TryHandlePocoTypeDispatchWithCompoundAlternatives(EncodedTextWriter writer, IClass umlClass, IReadOnlyCollection alternatives, RuleGenerationContext ruleGenerationContext) { if (alternatives.Count < 2) { diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs index 75128b60..db10565e 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.cs @@ -744,7 +744,7 @@ private void ProcessMultiElementAlternatives(EncodedTextWriter writer, IClass um return; } - if (this.TryHandlePocoTypeDispatchWithCompoundAlternatives(writer, umlClass, alternatives, ruleGenerationContext)) + if (TryHandlePocoTypeDispatchWithCompoundAlternatives(writer, umlClass, alternatives, ruleGenerationContext)) { return; } diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs b/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs index cd98787b..1a1e8b74 100644 --- a/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Wrapper/LibraryRedirectingExternalReferenceService.cs @@ -107,7 +107,10 @@ public void AddExternalReferenceToProcess(Uri currentLocation, string externalRe } else { - this.logger?.LogInformation("File {FileName} already processed", new FileInfo(resolvedUri.LocalPath).Name); + if (this.logger?.IsEnabled(LogLevel.Information) == true) + { + this.logger.LogInformation("File {FileName} already processed", new FileInfo(resolvedUri.LocalPath).Name); + } } } @@ -152,7 +155,10 @@ private Uri Redirect(Uri currentLocation, string unescapedHref) var subPath = unescapedHref.Substring(markerIndex + LibraryMarker.Length); var redirectedPath = Path.GetFullPath(Path.Combine(this.libraryRoot, subPath)); - this.logger?.LogInformation("Redirected href '{Href}' to '{Redirected}'", unescapedHref, redirectedPath); + if (this.logger?.IsEnabled(LogLevel.Information) == true) + { + this.logger.LogInformation("Redirected href '{Href}' to '{Redirected}'", unescapedHref, redirectedPath); + } return new Uri(redirectedPath); } From 59b2c49d8ed08c00d6fa0f38b5abdc25f987ac53 Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 11:37:36 +0200 Subject: [PATCH 7/8] nugetconfig changes --- Nuget.Config | 3 --- 1 file changed, 3 deletions(-) diff --git a/Nuget.Config b/Nuget.Config index 425a96fb..6cf91eb6 100644 --- a/Nuget.Config +++ b/Nuget.Config @@ -4,7 +4,4 @@ - - - \ No newline at end of file From 53d985fb917ca5365b13e1e7bcb78d4d477f3687 Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 3 Jun 2026 11:52:04 +0200 Subject: [PATCH 8/8] fix SQ issues --- .../HandleBarHelpers/RuleProcessor.ElementProcessing.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs index bf0692c1..2268fd97 100644 --- a/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs +++ b/SysML2.NET.CodeGenerator/HandleBarHelpers/RuleProcessor.ElementProcessing.cs @@ -596,7 +596,7 @@ internal void ProcessNonTerminalElement(EncodedTextWriter writer, IClass umlClas { if (NoTargetRuleResolver.IsSharedRule(referencedRule, umlClass)) { - this.EmitSharedNoTargetRuleCall(writer, umlClass, nonTerminalElement, referencedRule, ruleGenerationContext); + EmitSharedNoTargetRuleCall(writer, umlClass, nonTerminalElement, referencedRule, ruleGenerationContext); } else { @@ -692,7 +692,7 @@ internal void DeclareCursorIfRequired(EncodedTextWriter writer, IClass umlClass, /// The being processed /// The referenced shared no-target /// The current - private void EmitSharedNoTargetRuleCall(EncodedTextWriter writer, IClass umlClass, NonTerminalElement nonTerminalElement, TextualNotationRule referencedRule, RuleGenerationContext ruleGenerationContext) + private static void EmitSharedNoTargetRuleCall(EncodedTextWriter writer, IClass umlClass, NonTerminalElement nonTerminalElement, TextualNotationRule referencedRule, RuleGenerationContext ruleGenerationContext) { var effectiveTarget = NoTargetRuleResolver.ResolveEffectiveTarget(referencedRule, ruleGenerationContext.AllRules, umlClass);