Module: Lutaml::UmlRepository::IndexBuilders::AssociationIndex

Included in:
Lutaml::UmlRepository::IndexBuilder
Defined in:
lib/lutaml/uml_repository/index_builders/association_index.rb

Instance Method Summary collapse

Instance Method Details

#build_association_indexObject



7
8
9
10
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 7

def build_association_index
  index_document_associations
  index_class_level_associations
end

#build_inheritance_graph_indexObject

Build the inheritance graph index

Creates a hash mapping parent qualified names to arrays of child qualified names:

"ModelRoot::Parent" => ["ModelRoot::Child1", "ModelRoot::Child2"]


42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 42

def build_inheritance_graph_index
  # Process top-level classes
  if @document.classes
    process_generalizations(@document.classes,
                            IndexBuilder::ROOT_PACKAGE_NAME)
  end

  # Process classes in packages
  traverse_packages(@document.packages,
                    parent_path: IndexBuilder::ROOT_PACKAGE_NAME) do |package, path|
    process_generalizations(package.classes, path) if package.classes
  end
end

#extract_parent_name(generalization) ⇒ String?

Extract parent name from generalization object

Generalization object

Parameters:

Returns:

  • (String, nil)

    Parent class name



118
119
120
121
122
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 118

def extract_parent_name(generalization)
  return nil unless generalization

  name_from_general(generalization) || generalization.name
end

#index_class_level_associationsObject



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 20

def index_class_level_associations
  @qualified_names.each_value do |klass|
    next unless klassifiable?(klass) && klass.associations

    klass.associations.each do |assoc|
      next unless assoc.xmi_id

      @associations[assoc.xmi_id] ||= assoc
    end
  end
end

#index_document_associationsObject



12
13
14
15
16
17
18
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 12

def index_document_associations
  @document.associations&.each do |assoc|
    next unless assoc.xmi_id

    @associations[assoc.xmi_id] = assoc
  end
end

#index_generalization_edge(child_qname, klass, package_path) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 72

def index_generalization_edge(child_qname, klass, package_path)
  return unless klass.generalization

  parent_name = extract_parent_name(klass.generalization)
  return unless parent_name

  parent_qname = resolve_qualified_name(parent_name, package_path)
  return unless parent_qname && child_qname != parent_qname

  (@inheritance_graph[parent_qname] ||= []) << child_qname
end

#index_inheritance_assoc_edges(child_qname, klass, package_path) ⇒ Object



84
85
86
87
88
89
90
91
92
93
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 84

def index_inheritance_assoc_edges(child_qname, klass, package_path)
  return unless klass.associations

  klass.associations
    .select { |assoc| assoc.member_end_type == "inheritance" }
    .each do |assoc|
    index_inheritance_edge(child_qname, assoc,
                           package_path)
  end
end

#index_inheritance_edge(child_qname, assoc, package_path) ⇒ Object



95
96
97
98
99
100
101
102
103
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 95

def index_inheritance_edge(child_qname, assoc, package_path)
  parent_name = resolve_parent_name_from_assoc(assoc)
  return unless parent_name

  parent_qname = resolve_qualified_name(parent_name, package_path)
  return unless parent_qname && child_qname != parent_qname

  (@inheritance_graph[parent_qname] ||= []) << child_qname
end

#klassifiable?(klass) ⇒ Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 32

def klassifiable?(klass)
  klass.is_a?(Lutaml::Uml::Class) || klass.is_a?(Lutaml::Uml::DataType)
end

#name_from_general(generalization) ⇒ String?

Resolve a class name to its qualified name

This is a simplified resolution that checks:

  1. Same package

  2. Already qualified name in index

Parameters:

  • name (String)

    Class name to resolve

  • current_package_path (String)

    Current package context

Returns:

  • (String, nil)

    Resolved qualified name



133
134
135
136
137
138
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 133

def name_from_general(generalization)
  parent = generalization.general
  return nil unless parent

  parent.is_a?(String) ? parent : parent.name
end

#process_generalizations(classes, package_path) ⇒ Object

Process generalization relationships to build inheritance graph

Parameters:

  • classes (Array<Lutaml::Uml::Class>)

    Classes to process

  • package_path (String)

    Package path for these classes



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 60

def process_generalizations(classes, package_path)
  return unless classes

  classes.each do |klass|
    next unless klass.name

    child_qname = "#{package_path}::#{klass.name}"
    index_generalization_edge(child_qname, klass, package_path)
    index_inheritance_assoc_edges(child_qname, klass, package_path)
  end
end

#resolve_parent_name_from_assoc(assoc) ⇒ Object



105
106
107
108
109
110
111
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 105

def resolve_parent_name_from_assoc(assoc)
  parent_name = assoc.member_end
  return nil unless parent_name

  parent_name = parent_name.name if parent_name.is_a?(Lutaml::Uml::Generalization)
  parent_name.is_a?(String) && !parent_name.empty? ? parent_name : nil
end

#resolve_qualified_name(name, current_package_path) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/lutaml/uml_repository/index_builders/association_index.rb', line 140

def resolve_qualified_name(name, current_package_path)
  # If name contains "::", it might already be qualified
  return name if @qualified_names.key?(name)

  # Try in current package
  local_qname = "#{current_package_path}::#{name}"
  return local_qname if @qualified_names.key?(local_qname)

  # O(1) lookup using reverse index instead of O(n) scan
  candidates = @simple_name_to_qnames[name]
  candidates&.first
end