Class: SvgConform::ValidationContext

Inherits:
Object
  • Object
show all
Defined in:
lib/svg_conform/validation_context.rb

Overview

Context object passed to requirements during validation

The ValidationContext serves as the central state management object during SVG validation. It tracks errors, warnings, structural issues, and provides helper methods for requirements to report issues.

Architecture

ValidationContext delegates to specialized tracker classes:

  • ErrorTracker: tracks errors, warnings, and notices
  • StructuralInvalidityTracker: tracks structurally invalid nodes
  • NodeIdManager: handles node ID generation for both SAX and DOM modes
  • ReferenceManifest: tracks ID definitions and references
  • State Registry: manages requirement-specific state for deferred validation

Validation modes

DOM validation (remediation mode)

context = ValidationContext.new(document, profile)
requirement.validate_document(document, context)

SAX validation (streaming mode)

context = ValidationContext.new(nil, profile)
requirement.validate_sax_element(element, context)

State management for deferred validation

Requirements that need deferred validation (collecting data during SAX parsing and validating at document end) use requirement-specific State classes:

class MyRequirement < BaseRequirement
class State
  attr_accessor :collected_items

  def initialize
    @collected_items = []
  end
end

def collect_sax_data(element, context)
  state = context.state_for(self)
  state.collected_items << extract_from(element)
end

def validate_sax_complete(context)
  state = context.state_for(self)
  # Validate state.collected_items
end
end

The state_for method:

  • Returns a State instance specific to the requirement class
  • Creates the State instance on first access (lazy initialization)
  • Maintains one State instance per requirement class per validation
  • Prevents state pollution when reusing profiles across validations

Each validation gets a fresh ValidationContext with a fresh state registry, ensuring that reusing the same Profile for multiple validations doesn't leak state between validations.

Error reporting

context.add_error(
requirement_id: id,
message: "Element not allowed",
node: node,
severity: :error,
fix: -> { remove_element(node) }
)

Node ID tracking

The context generates unique node IDs for tracking:

  • SAX mode: uses pre-computed path_id from ElementProxy
  • DOM mode: uses DocumentAnalyzer with forward-counting algorithm

Structural invalidity

Requirements can mark nodes as structurally invalid (e.g., invalid parent-child relationships). Other requirements should skip validation on these nodes since they will be removed during remediation.

context.mark_node_structurally_invalid(node)
context.node_structurally_invalid?(node) # => true

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document, profile) ⇒ ValidationContext

Returns a new instance of ValidationContext.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/svg_conform/validation_context.rb', line 129

def initialize(document, profile)
  @document = document
  @profile = profile

  # Use TrackerFactory to create all tracker instances
  trackers = Validation::TrackerFactory.create_all_trackers(document)
  @error_tracker = trackers[:error_tracker]
  @node_id_manager = trackers[:node_id_manager]
  @structural_invalidity_tracker = trackers[:structural_invalidity_tracker]
  @reference_manifest = trackers[:reference_manifest]

  @fixes = []
  @state_registry = {} # Maps requirement class => state instance
end

Instance Attribute Details

#documentObject (readonly)

The document being validated (nil in SAX mode)



101
102
103
# File 'lib/svg_conform/validation_context.rb', line 101

def document
  @document
end

#fixesObject (readonly)

Array of fixes that can be applied



107
108
109
# File 'lib/svg_conform/validation_context.rb', line 107

def fixes
  @fixes
end

#profileObject (readonly)

The profile containing requirements to validate against



104
105
106
# File 'lib/svg_conform/validation_context.rb', line 104

def profile
  @profile
end

#reference_manifestObject (readonly)

Reference manifest tracking IDs and references



110
111
112
# File 'lib/svg_conform/validation_context.rb', line 110

def reference_manifest
  @reference_manifest
end

Instance Method Details

#add_error(node:, message:, rule: nil, requirement: nil, requirement_id: nil, severity: nil, fix: nil, data: {}) ⇒ Object



167
168
169
170
171
172
173
# File 'lib/svg_conform/validation_context.rb', line 167

def add_error(node:, message:, rule: nil, requirement: nil,
requirement_id: nil, severity: nil, fix: nil, data: {})
  @error_tracker.add_error(
    node: node, message: message, rule: rule, requirement: requirement,
    requirement_id: requirement_id, severity: severity, fix: fix, data: data
  )
end

#add_external_reference_notice(node:, reference:, message: nil) ⇒ Object

Add notice for external references (not errors)



232
233
234
235
236
237
238
239
# File 'lib/svg_conform/validation_context.rb', line 232

def add_external_reference_notice(node:, reference:, message: nil)
  @error_tracker.add_notice(
    rule: nil,
    node: node,
    message: message || "External reference: #{reference.value}",
    data: { reference: reference },
  )
end

#add_fix(fix) ⇒ Object



186
187
188
# File 'lib/svg_conform/validation_context.rb', line 186

def add_fix(fix)
  @fixes << fix
end

#add_notice(rule:, node:, message:, fix: nil, data: {}) ⇒ Object

Add an informational notice (delegates to ErrorTracker)



181
182
183
184
# File 'lib/svg_conform/validation_context.rb', line 181

def add_notice(rule:, node:, message:, fix: nil, data: {})
  @error_tracker.add_notice(rule: rule, node: node, message: message,
                            fix: fix, data: data)
end

#add_warning(rule:, node:, message:, fix: nil) ⇒ Object



175
176
177
178
# File 'lib/svg_conform/validation_context.rb', line 175

def add_warning(rule:, node:, message:, fix: nil)
  @error_tracker.add_warning(rule: rule, node: node, message: message,
                             fix: fix)
end

#errorsObject

Delegate error tracking methods to ErrorTracker



113
114
115
# File 'lib/svg_conform/validation_context.rb', line 113

def errors
  @error_tracker.errors
end

#fixable_countObject



206
207
208
# File 'lib/svg_conform/validation_context.rb', line 206

def fixable_count
  (@error_tracker.errors + @error_tracker.warnings).count(&:fixable?)
end

#generate_node_id(node) ⇒ Object

Generate a unique identifier for a node based on its path Delegates to NodeIdManager which handles both SAX and DOM modes



243
244
245
# File 'lib/svg_conform/validation_context.rb', line 243

def generate_node_id(node)
  @node_id_manager.generate_node_id(node)
end

#has_errors?Boolean

Returns:

  • (Boolean)


190
191
192
# File 'lib/svg_conform/validation_context.rb', line 190

def has_errors?
  @error_tracker.has_errors?
end

#has_fixes?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/svg_conform/validation_context.rb', line 198

def has_fixes?
  !@fixes.empty?
end

#has_warnings?Boolean

Returns:

  • (Boolean)


194
195
196
# File 'lib/svg_conform/validation_context.rb', line 194

def has_warnings?
  @error_tracker.has_warnings?
end

#id_defined?(id_value) ⇒ Boolean

Check if ID exists in manifest

Returns:

  • (Boolean)


227
228
229
# File 'lib/svg_conform/validation_context.rb', line 227

def id_defined?(id_value)
  @reference_manifest.id_defined?(id_value)
end

#infosObject



125
126
127
# File 'lib/svg_conform/validation_context.rb', line 125

def infos
  @error_tracker.infos
end

#issue_countObject



202
203
204
# File 'lib/svg_conform/validation_context.rb', line 202

def issue_count
  @error_tracker.total_error_count + warnings.size
end

#mark_descendants_invalid(node) ⇒ Object

Mark all descendants of a node as structurally invalid



158
159
160
# File 'lib/svg_conform/validation_context.rb', line 158

def mark_descendants_invalid(node)
  @structural_invalidity_tracker.mark_descendants_invalid(node)
end

#mark_node_structurally_invalid(node) ⇒ Object

Mark a node as structurally invalid (e.g., invalid parent-child relationship) Other requirements should skip attribute validation on these nodes Also marks all descendants as invalid since they'll be removed with the parent



153
154
155
# File 'lib/svg_conform/validation_context.rb', line 153

def mark_node_structurally_invalid(node)
  @structural_invalidity_tracker.mark_node_structurally_invalid(node)
end

#node_structurally_invalid?(node) ⇒ Boolean

Check if a node is structurally invalid

Returns:

  • (Boolean)


163
164
165
# File 'lib/svg_conform/validation_context.rb', line 163

def node_structurally_invalid?(node)
  @structural_invalidity_tracker.node_structurally_invalid?(node)
end

#register_id(id_value, element_name:, line_number: nil, column_number: nil) ⇒ Object

Register an ID definition



211
212
213
214
215
216
217
218
219
# File 'lib/svg_conform/validation_context.rb', line 211

def register_id(id_value, element_name:, line_number: nil,
column_number: nil)
  @reference_manifest.register_id(
    id_value,
    element_name: element_name,
    line_number: line_number,
    column_number: column_number,
  )
end

#register_reference(reference) ⇒ Object

Register a reference



222
223
224
# File 'lib/svg_conform/validation_context.rb', line 222

def register_reference(reference)
  @reference_manifest.register_reference(reference)
end

#state_for(requirement) ⇒ Object

Get or create state for a requirement Each requirement gets its own state instance, preventing state pollution



146
147
148
# File 'lib/svg_conform/validation_context.rb', line 146

def state_for(requirement)
  @state_registry[requirement.class] ||= requirement.class::State.new
end

#validity_errorsObject



121
122
123
# File 'lib/svg_conform/validation_context.rb', line 121

def validity_errors
  @error_tracker.validity_errors
end

#warningsObject



117
118
119
# File 'lib/svg_conform/validation_context.rb', line 117

def warnings
  @error_tracker.warnings
end