class Lutaml::Qea::Verification::StructureMatcher

by qualified name/path
Matches corresponding elements between XMI and QEA documents

def build_class_index(document) # rubocop:disable Metrics/MethodLength

rubocop:disable Metrics/MethodLength
Build class index from document and packages
def build_class_index(document) # rubocop:disable Metrics/MethodLength
  index = {}
  # Index top-level classes
  document.classes&.each do |klass|
    next unless klass.name
    index[klass.name] = klass
  end
  # Index classes in packages
  if document.packages
    index_classes_in_packages(document.packages, "",
                              index)
  end
  index
end

def build_data_type_index(document) # rubocop:disable Metrics/MethodLength

rubocop:disable Metrics/MethodLength
Build data type index from document and packages
def build_data_type_index(document) # rubocop:disable Metrics/MethodLength
  index = {}
  # Index top-level data types
  document.data_types&.each do |dt|
    next unless dt.name
    index[dt.name] = dt
  end
  # Index data types in packages
  if document.packages
    index_data_types_in_packages(document.packages, "",
                                 index)
  end
  index
end

def build_enum_index(document) # rubocop:disable Metrics/MethodLength

rubocop:disable Metrics/MethodLength
Build enum index from document and packages
def build_enum_index(document) # rubocop:disable Metrics/MethodLength
  index = {}
  # Index top-level enums
  document.enums&.each do |enum|
    next unless enum.name
    index[enum.name] = enum
  end
  # Index enums in packages
  if document.packages
    index_enums_in_packages(document.packages, "",
                            index)
  end
  index
end

def build_operation_signature(operation)

Build operation signature for matching
def build_operation_signature(operation)
  return operation.name unless operation.owned_parameter
  param_types = operation.owned_parameter.map do |param|
    param.type || "unknown"
  end.join(",")
  "#{operation.name}(#{param_types})"
end

def build_package_index(packages, parent_path = "") # rubocop:disable Metrics/MethodLength

rubocop:disable Metrics/MethodLength
Build package index with qualified paths
def build_package_index(packages, parent_path = "") # rubocop:disable Metrics/MethodLength
  index = {}
  return index unless packages
  packages.each do |package|
    next unless package.name
    qualified_path = build_path(parent_path, package.name)
    index[qualified_path] = package
    # Recursively index nested packages
    nested = build_package_index(package.packages, qualified_path)
    index.merge!(nested)
  end
  index
end

def build_path(parent_path, name)

Build qualified path from parent and name
def build_path(parent_path, name)
  return name if parent_path.empty?
  "#{parent_path}::#{name}"
end

def build_qualified_names(document)

Returns:
  • (Hash) - Hash mapping qualified names to elements

Parameters:
  • document (Lutaml::Uml::Document) -- The document
def build_qualified_names(document)
  {
    packages: build_package_index(document.packages),
    classes: build_class_index(document),
    enums: build_enum_index(document),
    data_types: build_data_type_index(document),
  }
end

def index_by_name(collection)

Index collection by name
def index_by_name(collection)
  index = {}
  collection.each do |element|
    next unless element.name
    index[element.name] = element
  end
  index
end

def index_by_signature(operations)

Index operations by signature (name + parameter types)
def index_by_signature(operations)
  index = {}
  operations.each do |operation|
    next unless operation.name
    signature = build_operation_signature(operation)
    index[signature] = operation
  end
  index
end

def index_classes_in_packages(packages, parent_path, index) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength

rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
Recursively index classes in packages
def index_classes_in_packages(packages, parent_path, index) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
  return unless packages
  packages.each do |package|
    next unless package.name
    package_path = build_path(parent_path, package.name)
    package.classes&.each do |klass|
      next unless klass.name
      qualified_name = "#{package_path}::#{klass.name}"
      index[qualified_name] = klass
    end
    # Recurse into nested packages
    index_classes_in_packages(package.packages, package_path, index)
  end
end

def index_data_types_in_packages(packages, parent_path, index) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength

rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
Recursively index data types in packages
def index_data_types_in_packages(packages, parent_path, index) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
  return unless packages
  packages.each do |package|
    next unless package.name
    package_path = build_path(parent_path, package.name)
    package.data_types&.each do |dt|
      next unless dt.name
      qualified_name = "#{package_path}::#{dt.name}"
      index[qualified_name] = dt
    end
    # Recurse into nested packages
    index_data_types_in_packages(package.packages, package_path, index)
  end
end

def index_enums_in_packages(packages, parent_path, index) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength

rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
Recursively index enums in packages
def index_enums_in_packages(packages, parent_path, index) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
  return unless packages
  packages.each do |package|
    next unless package.name
    package_path = build_path(parent_path, package.name)
    package.enums&.each do |enum|
      next unless enum.name
      qualified_name = "#{package_path}::#{enum.name}"
      index[qualified_name] = enum
    end
    # Recurse into nested packages
    index_enums_in_packages(package.packages, package_path, index)
  end
end

def match_attributes(xmi_class, qea_class)

Returns:
  • (Hash) - Hash with :matches, :xmi_only, :qea_only

Parameters:
  • qea_class (Lutaml::Uml::Class) -- QEA class
  • xmi_class (Lutaml::Uml::Class) -- XMI class
def match_attributes(xmi_class, qea_class)
  xmi_attrs = index_by_name(xmi_class.attributes || [])
  qea_attrs = index_by_name(qea_class.attributes || [])
  match_elements(xmi_attrs, qea_attrs)
end

def match_classes(xmi_doc, qea_doc)

Returns:
  • (Hash) - Hash with :matches, :xmi_only, :qea_only

Parameters:
  • qea_doc (Lutaml::Uml::Document) -- QEA document
  • xmi_doc (Lutaml::Uml::Document) -- XMI document
def match_classes(xmi_doc, qea_doc)
  xmi_classes = build_class_index(xmi_doc)
  qea_classes = build_class_index(qea_doc)
  match_elements(xmi_classes, qea_classes)
end

def match_elements(xmi_index, qea_index) # rubocop:disable Metrics/MethodLength

rubocop:disable Metrics/MethodLength
Match elements between two indexes
def match_elements(xmi_index, qea_index) # rubocop:disable Metrics/MethodLength
  matches = {}
  xmi_only = []
  qea_only = []
  # Find matches and XMI-only elements
  xmi_index.each do |key, xmi_element|
    if qea_index.key?(key)
      matches[key] = {
        xmi: xmi_element,
        qea: qea_index[key],
      }
    else
      xmi_only << key
    end
  end
  # Find QEA-only elements
  qea_index.each_key do |key|
    qea_only << key unless xmi_index.key?(key)
  end
  {
    matches: matches,
    xmi_only: xmi_only.sort,
    qea_only: qea_only.sort,
  }
end

def match_operations(xmi_class, qea_class)

Returns:
  • (Hash) - Hash with :matches, :xmi_only, :qea_only

Parameters:
  • qea_class (Lutaml::Uml::Class) -- QEA class
  • xmi_class (Lutaml::Uml::Class) -- XMI class
def match_operations(xmi_class, qea_class)
  xmi_ops = index_by_signature(xmi_class.operations || [])
  qea_ops = index_by_signature(qea_class.operations || [])
  match_elements(xmi_ops, qea_ops)
end

def match_packages(xmi_doc, qea_doc)

Returns:
  • (Hash) - Hash with :matches, :xmi_only, :qea_only

Parameters:
  • qea_doc (Lutaml::Uml::Document) -- QEA document
  • xmi_doc (Lutaml::Uml::Document) -- XMI document
def match_packages(xmi_doc, qea_doc)
  xmi_packages = build_package_index(xmi_doc.packages)
  qea_packages = build_package_index(qea_doc.packages)
  match_elements(xmi_packages, qea_packages)
end