class Lutaml::ModelTransformations::Parsers::QeaParser
error handling, progress tracking, and configuration integration.
to the new unified transformation architecture. It provides enhanced
This parser wraps the existing Lutaml::Qea functionality and adapts it
database files.
QEA Parser implements the BaseParser interface for Enterprise Architect
def add_info(message, context = {})
-
(void)-
Parameters:
-
context(Hash) -- Additional context -
message(String) -- Info message
def add_info(message, context = {}) # For now, treat as warnings since base parser doesn't have info level add_warning(message, context) end
def add_qea_metadata(document, file_path) # rubocop:disable Metrics/MethodLength
-
(void)-
Parameters:
-
file_path(String) -- Source file path -
document(Lutaml::Uml::Document) -- Document to enhance
def add_qea_metadata(document, file_path) # rubocop:disable Metrics/MethodLength metadata = { source_file: file_path, source_format: "Enterprise Architect Database", parsed_at: Time.now, parser: self.class.name, parser_version: "1.0", database_stats: @database_stats, qea_version: detect_qea_version, options: @options, } # Store metadata using various approaches if document.class.method_defined?(:parsing_metadata=) document.parsing_metadata = metadata elsif document.class.method_defined?(:metadata=) document.metadata = metadata end end
def add_transformation_statistics(document) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
-
(void)-
Parameters:
-
document(Lutaml::Uml::Document) -- Document to analyze
def add_transformation_statistics(document) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity doc_stats = { packages: document.packages&.size || 0, classes: document.classes&.size || 0, data_types: document.data_types&.size || 0, enumerations: document.enums&.size || 0, associations: document.associations&.size || 0, diagrams: document.diagrams&.size || 0, } comparison = compare_stats_with_database(doc_stats) add_info("QEA transformation completed: " \ "#{format_statistics(doc_stats)}") if comparison.any? add_info("Database comparison: #{comparison.join(', ')}") end end
def after_parse(document, file_path)
-
(Lutaml::Uml::Document)- Processed document
Parameters:
-
file_path(String) -- Path to the source file -
document(Lutaml::Uml::Document) -- Parsed document
def after_parse(document, file_path) # Add QEA-specific metadata add_qea_metadata(document, file_path) # Validate QEA-specific aspects if @options[:validate_transformation] validate_qea_transformation(document) end # Add comprehensive statistics add_transformation_statistics(document) document end
def apply_custom_transformers(_factory)
-
(void)-
Parameters:
-
factory(Lutaml::Qea::Factory::EaToUmlFactory) --
def apply_custom_transformers(_factory) # This is a placeholder for future custom transformer support # Could allow configuration-driven transformer customization add_info("Custom transformers would be applied here") end
def before_parse(file_path) # rubocop:disable Metrics/MethodLength
-
(void)-
Parameters:
-
file_path(String) -- Path to the file being parsed
def before_parse(file_path) # rubocop:disable Metrics/MethodLength add_info("Starting QEA parsing for: #{file_path}") # Check file size and provide estimates file_size = File.size(file_path) add_info("QEA file size: #{format_file_size(file_size)}") if file_size > 500 * 1024 * 1024 # 500MB add_warning("Very large QEA file detected, " \ "parsing may take significant time") end # Quick database info check begin quick_stats = get_quick_database_stats(file_path) add_info("Database contains approximately: " \ "#{format_database_stats(quick_stats)}") rescue StandardError => e add_warning("Could not get quick database stats: #{e.message}") end end
def compare_stats_with_database(doc_stats) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
-
(Array- Comparison notes)
Parameters:
-
doc_stats(Hash) -- Document statistics
def compare_stats_with_database(doc_stats) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength return [] unless @database_stats comparisons = [] if @database_stats["packages"] ratio = doc_stats[:packages].to_f / @database_stats["packages"] comparisons << "packages #{(ratio * 100).round(1)}%" end if @database_stats["objects"] ratio = doc_stats[:classes].to_f / @database_stats["objects"] comparisons << "classes #{(ratio * 100).round(1)}%" end comparisons end
def content_patterns
def content_patterns [/^SQLite format/] end
def create_progress_callback
-
(Proc)- Progress callback procedure
def create_progress_callback proc do |table_name, current, total| percentage = (current.to_f / total * 100).round(1) add_info("Loading #{table_name}: #{current}/#{total} " \ "(#{percentage}%)") # Check if we should fail fast on too many errors if should_fail_fast? && has_errors? raise Parsers::ParseError.new("Failing fast due to errors " \ "during loading") end end end
def default_options
-
(Hash)- Default options hash
def default_options super.merge( include_diagrams: true, validate_transformation: false, load_progress_callback: true, cache_database: false, strict_schema_validation: false, ) end
def detect_qea_version
-
(String)- Detected version or "unknown"
def detect_qea_version return "unknown" unless @qea_database # This is a simplified version detection # In practice, you might check specific tables or metadata "EA Database" end
def extract_document_name(file_path)
-
(String)- Document name
Parameters:
-
file_path(String) -- File path
def extract_document_name(file_path) @options[:document_name] || File.basename(file_path, ".*") end
def format_database_stats(stats)
-
(String)- Formatted string
Parameters:
-
stats(Hash) -- Statistics hash
def format_database_stats(stats) return "unknown structure" if stats.empty? parts = [] parts << "#{stats['objects']} objects" if stats["objects"] parts << "#{stats['packages']} packages" if stats["packages"] parts << "#{stats['connectors']} connectors" if stats["connectors"] parts.join(", ") end
def format_file_size(size)
-
(String)- Formatted size string
Parameters:
-
size(Integer) -- Size in bytes
def format_file_size(size) units = %w[B KB MB GB] size = size.to_f unit_index = 0 while size >= 1024 && unit_index < units.length - 1 size /= 1024 unit_index += 1 end "#{size.round(1)} #{units[unit_index]}" end
def format_name
-
(String)- Human-readable format name
def format_name "Enterprise Architect Database (QEA)" end
def format_statistics(stats)
-
(String)- Formatted statistics string
Parameters:
-
stats(Hash) -- Statistics hash
def format_statistics(stats) parts = stats.map { |key, value| "#{value} #{key}" } parts.join(", ") end
def get_quick_database_stats(file_path)
-
(Hash)- Quick statistics
Parameters:
-
file_path(String) -- Path to QEA file
def get_quick_database_stats(file_path) Lutaml::Qea.database_info(file_path) end
def load_qea_database(file_path) # rubocop:disable Metrics/MethodLength
-
(Lutaml::Qea::Database)- Loaded database
Parameters:
-
file_path(String) -- Path to QEA file
def load_qea_database(file_path) # rubocop:disable Metrics/MethodLength progress_callback = nil if @options[:load_progress_callback] progress_callback = create_progress_callback end # Load database using existing QEA infrastructure if progress_callback Lutaml::Qea.load_database(file_path, &progress_callback) else Lutaml::Qea.load_database(file_path) end rescue StandardError => e add_error("Failed to load QEA database: #{e.message}") raise Parsers::ParseError.new("QEA database loading failed", original_error: e) end
def parse_internal(file_path)
-
(Lutaml::Uml::Document)- Parsed UML document
Parameters:
-
file_path(String) -- Path to the QEA file
def parse_internal(file_path) # Validate QEA file format validate_qea_format!(file_path) # Load QEA database with progress tracking @qea_database = load_qea_database(file_path) # Get database statistics @database_stats = @qea_database.stats # Transform to UML document using existing factory document = transform_qea_to_uml(@qea_database, file_path) # Post-process document post_process_qea_document(document, file_path) document end
def post_process_qea_document(document, file_path)
-
(void)-
Parameters:
-
file_path(String) -- Source file path -
document(Lutaml::Uml::Document) -- Document to process
def post_process_qea_document(document, file_path) # Set QEA-specific source information if document.class.method_defined?(:source_file=) document.source_file = file_path end if document.class.method_defined?(:source_format=) document.source_format = "QEA" end # Store database statistics if document.class.method_defined?(:database_stats=) document.database_stats = @database_stats end end
def prepare_transformation_options(file_path)
-
(Hash)- Transformation options
Parameters:
-
file_path(String) -- Source file path
def prepare_transformation_options(file_path) { include_diagrams: @options[:include_diagrams], validate: @options[:validate_output], document_name: extract_document_name(file_path), } end
def priority
def priority 90 end
def supported_extensions
-
(Array- List of extensions)
def supported_extensions [".qea", ".eap", ".eapx"] end
def transform_qea_to_uml(database, file_path) # rubocop:disable Metrics/MethodLength
-
(Lutaml::Uml::Document)- UML document
Parameters:
-
file_path(String) -- Source file path -
database(Lutaml::Qea::Database) -- QEA database
def transform_qea_to_uml(database, file_path) # rubocop:disable Metrics/MethodLength # Prepare transformation options transform_options = prepare_transformation_options(file_path) # Use existing QEA factory for transformation factory = Lutaml::Qea::Factory::EaToUmlFactory.new(database, transform_options) # Apply custom transformers if configured apply_custom_transformers(factory) if @options[:custom_transformers] # Execute transformation factory.create_document rescue StandardError => e add_error("Failed to transform QEA to UML: #{e.message}") raise Parsers::ParseError.new("QEA transformation failed", original_error: e) end
def validate_qea_format!(file_path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
-
(ParseError)- if file is not valid QEA
Parameters:
-
file_path(String) -- Path to validate
def validate_qea_format!(file_path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength # Check if it's a SQLite database (QEA files are SQLite) File.open(file_path, "rb") do |file| header = file.read(16) unless header == "SQLite format 3\0" add_error("File does not appear to be a SQLite database") raise Parsers::ParseError.new("Invalid QEA format - " \ "not a SQLite database") end end # Additional validation using QEA infrastructure begin Lutaml::Qea.connect(file_path).with_connection do |db| # Check for required EA tables required_tables = %w[t_object t_package t_connector t_attribute] missing_tables = required_tables.reject do |table| db.execute( "SELECT name FROM sqlite_master " \ "WHERE type='table' AND name=?", table ).any? end if missing_tables.any? add_warning("Missing expected EA tables: " \ "#{missing_tables.join(', ')}") end end rescue StandardError => e add_error("Failed to validate QEA database structure: #{e.message}") raise Parsers::ParseError.new("QEA validation failed", original_error: e) end end
def validate_qea_transformation(document) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
-
(void)-
Parameters:
-
document(Lutaml::Uml::Document) -- Document to validate
def validate_qea_transformation(document) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity # Compare document stats with database stats if @database_stats doc_packages = document.packages&.size || 0 db_packages = @database_stats["packages"] || 0 if doc_packages < db_packages add_warning("Document has fewer packages (#{doc_packages}) " \ "than database (#{db_packages})") end doc_classes = document.classes&.size || 0 db_objects = @database_stats["objects"] || 0 if doc_classes < db_objects * 0.8 # Allow some variance add_warning("Document classes (#{doc_classes}) significantly " \ "fewer than database objects (#{db_objects})") end end end