4747from strictdoc .helpers .cast import assert_cast
4848from strictdoc .helpers .exception import StrictDocException
4949from strictdoc .helpers .google_test import convert_function_name_to_gtest_macro
50+ from strictdoc .helpers .mid import MID
5051from strictdoc .helpers .ordered_set import OrderedSet
5152
5253if TYPE_CHECKING :
@@ -608,15 +609,50 @@ def validate_and_resolve(
608609 continue
609610
610611 assert source_node_ .entity_name is not None
612+ sdoc_node = None
611613 sdoc_node_uid = source_node_ .get_sdoc_field (
612614 "UID" , relevant_source_node_entry
613615 )
614- if sdoc_node_uid is None :
615- sdoc_node_uid = f"{ document_uid } /{ path_to_source_file_ } /{ source_node_ .entity_name } "
616- sdoc_node = traceability_index .get_node_by_uid_weak (
617- sdoc_node_uid
616+ mid = source_node_ .get_sdoc_field (
617+ "MID" , relevant_source_node_entry
618618 )
619619
620+ # First merge criterion: Merge if SDoc node with same MID exists.
621+ if mid is not None :
622+ sdoc_node_mid = MID (mid )
623+ merge_candidate_sdoc_node = (
624+ traceability_index .get_node_by_mid_weak (sdoc_node_mid )
625+ )
626+ if merge_candidate_sdoc_node is not None :
627+ merge_candidate_sdoc_node = assert_cast (
628+ merge_candidate_sdoc_node , SDocNode
629+ )
630+ # Simplification: Either SDoc must have a static UID that is not overwritten by source node.
631+ if (
632+ merge_candidate_sdoc_node .reserved_uid is not None
633+ and (
634+ merge_candidate_sdoc_node .reserved_uid
635+ == sdoc_node_uid
636+ or sdoc_node_uid is None
637+ )
638+ ):
639+ sdoc_node_uid = (
640+ merge_candidate_sdoc_node .reserved_uid
641+ )
642+ sdoc_node = merge_candidate_sdoc_node
643+
644+ if sdoc_node is None :
645+ # If no UID from source code field or merge-by-MID, create UID by conventional scheme.
646+ if sdoc_node_uid is None :
647+ sdoc_node_uid = f"{ document_uid } /{ path_to_source_file_ } /{ source_node_ .entity_name } "
648+ # Second merge criterion: Merge if SDoc node with same UID exists.
649+ tmp_sdoc_node = traceability_index .get_node_by_uid_weak (
650+ sdoc_node_uid
651+ )
652+ if isinstance (tmp_sdoc_node , SDocNode ):
653+ sdoc_node = tmp_sdoc_node
654+
655+ assert sdoc_node_uid is not None
620656 if sdoc_node is not None :
621657 sdoc_node = assert_cast (sdoc_node , SDocNode )
622658 self .merge_sdoc_node_with_source_node (
@@ -633,11 +669,6 @@ def validate_and_resolve(
633669 document ,
634670 )
635671 sdoc_node_uid = assert_cast (sdoc_node .reserved_uid , str )
636- traceability_index .graph_database .create_link (
637- link_type = GraphLinkType .UID_TO_NODE ,
638- lhs_node = sdoc_node_uid ,
639- rhs_node = sdoc_node ,
640- )
641672 if current_top_node is None :
642673 current_top_node = (
643674 FileTraceabilityIndex .create_source_node_section (
@@ -971,6 +1002,11 @@ def create_sdoc_node_from_source_node(
9711002 ):
9721003 sdoc_node_fields ["TITLE" ] = source_node .entity_name
9731004 FileTraceabilityIndex .set_sdoc_node_fields (sdoc_node , sdoc_node_fields )
1005+ if "MID" in sdoc_node_fields :
1006+ mid = sdoc_node_fields ["MID" ]
1007+ sdoc_node .reserved_mid = MID (mid )
1008+ if parent_document .config .enable_mid :
1009+ sdoc_node .mid_permanent = True
9741010 return sdoc_node
9751011
9761012 @staticmethod
@@ -1007,6 +1043,10 @@ def merge_sdoc_node_with_source_node(
10071043 sdoc_node_fields = source_node .get_sdoc_fields (source_node_config_entry )
10081044 FileTraceabilityIndex .set_sdoc_node_fields (sdoc_node , sdoc_node_fields )
10091045
1046+ if "MID" in sdoc_node_fields :
1047+ mid = sdoc_node_fields ["MID" ]
1048+ sdoc_node .reserved_mid = MID (mid )
1049+
10101050 @staticmethod
10111051 def set_sdoc_node_fields (
10121052 sdoc_node : SDocNode , sdoc_node_fields : dict [str , str ]
@@ -1088,6 +1128,31 @@ def connect_source_node_requirements(
10881128
10891129 Here we link REQ and sdoc_node bidirectional.
10901130 """
1131+ if (
1132+ sdoc_node .reserved_uid is not None
1133+ and not traceability_index .graph_database .has_link (
1134+ link_type = GraphLinkType .UID_TO_NODE ,
1135+ lhs_node = sdoc_node .reserved_uid ,
1136+ rhs_node = sdoc_node ,
1137+ )
1138+ ):
1139+ traceability_index .graph_database .create_link (
1140+ link_type = GraphLinkType .UID_TO_NODE ,
1141+ lhs_node = sdoc_node .reserved_uid ,
1142+ rhs_node = sdoc_node ,
1143+ )
1144+
1145+ if not traceability_index .graph_database .has_link (
1146+ link_type = GraphLinkType .MID_TO_NODE ,
1147+ lhs_node = sdoc_node .reserved_mid ,
1148+ rhs_node = sdoc_node ,
1149+ ):
1150+ traceability_index .graph_database .create_link (
1151+ link_type = GraphLinkType .MID_TO_NODE ,
1152+ lhs_node = sdoc_node .reserved_mid ,
1153+ rhs_node = sdoc_node ,
1154+ )
1155+
10911156 for marker_ in source_node .markers :
10921157 if not isinstance (marker_ , FunctionRangeMarker ):
10931158 continue
0 commit comments