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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +79 -13
- data/Gemfile +2 -2
- data/docs/sax_validation_mode.adoc +576 -0
- data/lib/svg_conform/document.rb +25 -1
- data/lib/svg_conform/document_analyzer.rb +118 -0
- data/lib/svg_conform/errors/base.rb +58 -0
- data/lib/svg_conform/errors/validation_issue.rb +245 -0
- data/lib/svg_conform/errors/validation_notice.rb +30 -0
- data/lib/svg_conform/interfaces/requirement_interface.rb +177 -0
- data/lib/svg_conform/node_helpers.rb +72 -0
- data/lib/svg_conform/remediations/base_remediation.rb +5 -35
- data/lib/svg_conform/remediations/namespace_remediation.rb +61 -0
- data/lib/svg_conform/requirements/base_requirement.rb +21 -41
- data/lib/svg_conform/requirements/color_restrictions_requirement.rb +19 -61
- data/lib/svg_conform/requirements/font_family_requirement.rb +14 -24
- data/lib/svg_conform/requirements/no_external_css_requirement.rb +19 -77
- data/lib/svg_conform/requirements/viewbox_required_requirement.rb +16 -72
- data/lib/svg_conform/sax_validation_handler.rb +15 -7
- data/lib/svg_conform/validation/error_tracker.rb +103 -0
- data/lib/svg_conform/validation/node_id_manager.rb +35 -0
- data/lib/svg_conform/validation/structural_invalidity_tracker.rb +63 -0
- data/lib/svg_conform/validation_context.rb +112 -459
- data/lib/svg_conform/validation_issue.rb +12 -0
- data/lib/svg_conform/version.rb +1 -1
- data/lib/svg_conform.rb +2 -0
- metadata +13 -3
- data/lib/svg_conform/fast_document_analyzer.rb +0 -82
|
@@ -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
|