class Lutaml::Cli::TreeViewFormatter

TreeViewFormatter formats UML repository contents as a colored tree

def colorize(text, color_key)

Colorize text if colors are enabled
def colorize(text, color_key)
  return text if @no_color
  color = COLORS[color_key]
  return text unless color
  Paint[text, color]
end

def determine_class_type(klass)

Determine class type (class, interface, enumeration)
def determine_class_type(klass)
  return :enumeration if enumeration?(klass)
  return :interface if interface?(klass)
  :class
end

def enumeration?(klass)

def enumeration?(klass)
  klass.class.name&.include?("Enum")
end

def find_package_path(repository, package)

Find package path for a package object
def find_package_path(repository, package)
  repository.indexes[:package_paths].each do |path, pkg|
    return path if pkg == package
  end
  nil
end

def format(repository) # rubocop:disable Metrics/MethodLength

Returns:
  • (String) - Formatted tree output

Parameters:
  • repository (Lutaml::UmlRepository::Repository) --
def format(repository) # rubocop:disable Metrics/MethodLength
  output = []
  # Start with ModelRoot
  output << colorize("ModelRoot", :package)
  # Get all top-level packages
  root_packages = repository.list_packages("ModelRoot", recursive: false)
  root_packages.each_with_index do |pkg, idx|
    is_last = idx == root_packages.size - 1
    output << format_package(pkg, repository, 0, is_last, "")
  end
  # Add statistics at the end
  output << ""
  output << format_statistics(repository.statistics)
  output.join("\n")
end

def format_association(assoc, depth, is_last, prefix) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity

rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
Format an association node
def format_association(assoc, depth, is_last, prefix) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
  return "" if @max_depth && depth >= @max_depth
  assoc_name = assoc.name || "(unnamed)"
  target = assoc.member_end || "Unknown"
  connector = is_last ? TREE_CHARS[:last_branch] : TREE_CHARS[:branch]
  icon = ICONS[:association]
  "#{prefix}#{connector} #{icon} #{colorize("#{assoc_name} → #{target}",
                                            :association)}"
end

def format_attribute(attr, depth, is_last, prefix)

Format an attribute node
def format_attribute(attr, depth, is_last, prefix)
  return "" if @max_depth && depth >= @max_depth
  attr_name = attr.name || "(unnamed)"
  attr_type = attr.type || "Unknown"
  connector = is_last ? TREE_CHARS[:last_branch] : TREE_CHARS[:branch]
  icon = ICONS[:attribute]
  "#{prefix}#{connector} #{icon} #{colorize(
    "#{attr_name} : #{attr_type}", :attribute
  )}"
end

def format_class(klass, repository, depth, is_last, prefix, package_path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity,Metrics/ParameterLists

rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity,Metrics/ParameterLists
Format a class node
def format_class(klass, repository, depth, is_last, prefix, package_path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity,Metrics/ParameterLists
  return "" if @max_depth && depth >= @max_depth
  output = []
  class_name = klass.name || "(unnamed)"
  # Determine class type
  type = determine_class_type(klass)
  icon = ICONS[type] || ICONS[:class]
  color = COLORS[type] || COLORS[:class]
  # Class line
  connector = is_last ? TREE_CHARS[:last_branch] : TREE_CHARS[:branch]
  type_label = type == :class ? "Class" : type.to_s.capitalize
  output << "#{prefix}#{connector} #{icon} " \
            "#{colorize(class_name, color)} (#{type_label})"
  # Update prefix for children
  child_prefix = prefix + (
    is_last ? TREE_CHARS[:space] : TREE_CHARS[:continuation]
  )
  # Collect children
  children = []
  # Add attributes
  if @show_attributes && klass.is_a?(Lutaml::Uml::Classifier) &&
      klass.attributes
    children.concat(klass.attributes.map do |a|
      { type: :attribute, obj: a }
    end)
  end
  # Add operations
  if @show_operations && klass.is_a?(Lutaml::Uml::Classifier) &&
      klass.operations
    children.concat(klass.operations.map do |o|
      { type: :operation, obj: o }
    end)
  end
  # Add associations
  if @show_associations
    qualified_name = "#{package_path}::#{class_name}"
    associations = repository.associations_of(qualified_name)
    children.concat(associations.map do |a|
      { type: :association, obj: a }
    end)
  end
  # Format each child
  children.each_with_index do |child, idx|
    is_last_child = idx == children.size - 1
    case child[:type]
    when :attribute
      output << format_attribute(child[:obj], depth + 1, is_last_child,
                                 child_prefix)
    when :operation
      output << format_operation(child[:obj], depth + 1, is_last_child,
                                 child_prefix)
    when :association
      output << format_association(child[:obj], depth + 1, is_last_child,
                                   child_prefix)
    end
  end
  output.join("\n")
end

def format_diagram(diagram, depth, is_last, prefix)

Format a diagram node
def format_diagram(diagram, depth, is_last, prefix)
  return "" if @max_depth && depth >= @max_depth
  diag_name = diagram.name || "(unnamed)"
  connector = is_last ? TREE_CHARS[:last_branch] : TREE_CHARS[:branch]
  icon = ICONS[:diagram]
  "#{prefix}#{connector} #{icon} #{colorize(diag_name, :diagram)}"
end

def format_operation(op, depth, is_last, prefix)

Format an operation node
def format_operation(op, depth, is_last, prefix)
  return "" if @max_depth && depth >= @max_depth
  op_name = op.name || "(unnamed)"
  connector = is_last ? TREE_CHARS[:last_branch] : TREE_CHARS[:branch]
  icon = ICONS[:operation]
  "#{prefix}#{connector} #{icon} #{colorize("#{op_name}()", :operation)}"
end

def format_package(package, repository, depth, is_last, prefix) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity

rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
Format a package node
def format_package(package, repository, depth, is_last, prefix) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
  return "" if @max_depth && depth >= @max_depth
  output = []
  pkg_name = package.name || "(unnamed)"
  # Package line
  connector = is_last ? TREE_CHARS[:last_branch] : TREE_CHARS[:branch]
  icon = ICONS[:package]
  output << "#{prefix}#{connector} #{icon} #{colorize(pkg_name,
                                                      :package)}"
  # Update prefix for children
  child_prefix = prefix + (
    is_last ? TREE_CHARS[:space] : TREE_CHARS[:continuation]
  )
  # Get package path for this package
  pkg_path = find_package_path(repository, package)
  return output.join("\n") unless pkg_path
  # Collect all children
  children = []
  # Add sub-packages
  sub_packages = repository.list_packages(pkg_path, recursive: false)
  children.concat(sub_packages.map { |p| { type: :package, obj: p } })
  # Add classes
  classes = repository.classes_in_package(pkg_path, recursive: false)
  children.concat(classes.map { |c| { type: :class, obj: c } })
  # Add diagrams if at top level
  if depth.zero?
    diagrams = repository.diagrams_in_package(pkg_path)
    children.concat(diagrams.map { |d| { type: :diagram, obj: d } })
  end
  # Format each child
  children.each_with_index do |child, idx|
    is_last_child = idx == children.size - 1
    case child[:type]
    when :package
      output << format_package(child[:obj], repository, depth + 1,
                               is_last_child, child_prefix)
    when :class
      output << format_class(child[:obj], repository, depth + 1,
                             is_last_child, child_prefix, pkg_path)
    when :diagram
      output << format_diagram(child[:obj], depth + 1,
                               is_last_child, child_prefix)
    end
  end
  output.join("\n")
end

def format_statistics(stats)

Format statistics section
def format_statistics(stats)
  output = []
  output << colorize("📊 Statistics", :statistics)
  output << "#{TREE_CHARS[:branch]} Total Packages: " \
            "#{stats[:total_packages]}"
  output << "#{TREE_CHARS[:branch]} Total Classes: " \
            "#{stats[:total_classes]}"
  output << "#{TREE_CHARS[:last_branch]} Total Diagrams: " \
            "#{stats[:total_diagrams]}"
  output.join("\n")
end

def initialize(options = {})

def initialize(options = {})
  @max_depth = options[:max_depth]
  @show_attributes = options.fetch(:show_attributes, true)
  @show_operations = options.fetch(:show_operations, true)
  @show_associations = options.fetch(:show_associations, false)
  @no_color = options.fetch(:no_color, false)
  @current_depth = 0
end

def interface?(klass)

def interface?(klass)
  klass.is_a?(Lutaml::Uml::TopElement) &&
    Array(klass.stereotype).any? { |s| s&.downcase == "interface" }
end