svg_conform 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SvgConform
4
+ module Validation
5
+ # Tracks validation errors, warnings, and informational messages
6
+ # Separated from ValidationContext for better separation of concerns
7
+ #
8
+ # Note: This class references ValidationContext::ValidationIssue which is
9
+ # defined in ValidationContext. ValidationContext must be loaded first.
10
+ class ErrorTracker
11
+ attr_reader :errors, :warnings, :validity_errors, :infos
12
+
13
+ def initialize
14
+ @errors = []
15
+ @warnings = []
16
+ @validity_errors = []
17
+ @infos = []
18
+ end
19
+
20
+ # Add an error to the context
21
+ def add_error(node:, message:, rule: nil, requirement: nil,
22
+ requirement_id: nil, severity: nil, fix: nil, data: {})
23
+ # Support both old rule system and new requirements system
24
+ rule_or_requirement = requirement || rule
25
+
26
+ error = ::SvgConform::Errors::ValidationIssue.new(
27
+ type: :error,
28
+ rule: rule_or_requirement,
29
+ node: node,
30
+ message: message,
31
+ fix: fix,
32
+ data: data,
33
+ requirement_id: requirement_id,
34
+ severity: severity,
35
+ )
36
+
37
+ # Handle special severity types
38
+ if severity == :validity_error
39
+ @validity_errors << error
40
+ else
41
+ @errors << error
42
+ end
43
+
44
+ error
45
+ end
46
+
47
+ # Add a warning to the context
48
+ def add_warning(rule:, node:, message:, fix: nil)
49
+ warning = ::SvgConform::Errors::ValidationIssue.new(
50
+ type: :warning,
51
+ rule: rule,
52
+ node: node,
53
+ message: message,
54
+ fix: fix,
55
+ )
56
+ @warnings << warning
57
+ warning
58
+ end
59
+
60
+ # Add an informational notice to the context
61
+ def add_notice(rule:, node:, message:, fix: nil, data: {}, type: :info)
62
+ notice = ::SvgConform::Errors::ValidationIssue.new(
63
+ type: type,
64
+ rule: rule,
65
+ node: node,
66
+ message: message,
67
+ fix: fix,
68
+ data: data,
69
+ )
70
+ @infos << notice
71
+ notice
72
+ end
73
+
74
+ # Check if there are any errors
75
+ def has_errors?
76
+ @errors.any?
77
+ end
78
+
79
+ # Check if there are any warnings
80
+ def has_warnings?
81
+ @warnings.any?
82
+ end
83
+
84
+ # Check if there are any validity errors
85
+ def has_validity_errors?
86
+ @validity_errors.any?
87
+ end
88
+
89
+ # Get total error count (including validity errors)
90
+ def total_error_count
91
+ @errors.length + @validity_errors.length
92
+ end
93
+
94
+ # Clear all tracked issues
95
+ def clear
96
+ @errors.clear
97
+ @warnings.clear
98
+ @validity_errors.clear
99
+ @infos.clear
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../document_analyzer"
4
+
5
+ module SvgConform
6
+ module Validation
7
+ # Manages node ID generation for validation contexts
8
+ # Wraps DocumentAnalyzer for DOM mode and handles ElementProxy for SAX mode
9
+ class NodeIdManager
10
+ def initialize(document = nil)
11
+ @document = document
12
+ @analyzer = nil # Lazy-loaded, only needed for DOM/remediation mode
13
+ end
14
+
15
+ # Generate a unique identifier for a node based on its path
16
+ # Uses DocumentAnalyzer for efficient forward-counting algorithm (DOM mode)
17
+ # For SAX mode, uses the pre-computed path from ElementProxy
18
+ def generate_node_id(node)
19
+ # In SAX mode, node may be an ElementProxy with pre-computed path
20
+ return node.path_id if node.respond_to?(:path_id)
21
+
22
+ # Lazy-load the analyzer only when needed (DOM/remediation mode)
23
+ @analyzer ||= DocumentAnalyzer.new(@document) if @document
24
+ return nil unless @analyzer
25
+
26
+ @analyzer.get_node_id(node)
27
+ end
28
+
29
+ # Check if the manager has a document for DOM-based ID generation
30
+ def dom_mode?
31
+ !@document.nil?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module SvgConform
6
+ module Validation
7
+ # Tracks structurally invalid nodes during validation
8
+ # Separated from ValidationContext for better separation of concerns
9
+ class StructuralInvalidityTracker
10
+ def initialize(node_id_generator:)
11
+ @structurally_invalid_node_ids = Set.new
12
+ @node_id_generator = node_id_generator
13
+ end
14
+
15
+ # Mark a node as structurally invalid (e.g., invalid parent-child relationship)
16
+ # Other requirements should skip attribute validation on these nodes
17
+ # Also marks all descendants as invalid since they'll be removed with the parent
18
+ def mark_node_structurally_invalid(node)
19
+ node_id = @node_id_generator.call(node)
20
+ return if node_id.nil? # Safety check
21
+
22
+ @structurally_invalid_node_ids.add(node_id)
23
+
24
+ # Mark all descendants as invalid too
25
+ mark_descendants_invalid(node)
26
+ end
27
+
28
+ # Mark all descendants of a node as structurally invalid
29
+ def mark_descendants_invalid(node)
30
+ # In SAX mode, ElementProxy doesn't have children yet
31
+ # Children will be validated individually and will check parent validity
32
+ return unless node.respond_to?(:children) && node.children
33
+
34
+ node.children.each do |child|
35
+ child_id = @node_id_generator.call(child)
36
+ next if child_id.nil? # Skip if can't generate ID
37
+
38
+ @structurally_invalid_node_ids.add(child_id)
39
+ # Recursively mark descendants
40
+ mark_descendants_invalid(child)
41
+ end
42
+ end
43
+
44
+ # Check if a node is structurally invalid
45
+ def node_structurally_invalid?(node)
46
+ node_id = @node_id_generator.call(node)
47
+ return false if node_id.nil? # Safety check
48
+
49
+ @structurally_invalid_node_ids.include?(node_id)
50
+ end
51
+
52
+ # Clear all tracked invalid nodes
53
+ def clear
54
+ @structurally_invalid_node_ids.clear
55
+ end
56
+
57
+ # Get the count of structurally invalid nodes
58
+ def count
59
+ @structurally_invalid_node_ids.size
60
+ end
61
+ end
62
+ end
63
+ end