xsdvi 1.0.0 → 1.0.1

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.
@@ -29,7 +29,7 @@ module Xsdvi
29
29
 
30
30
  def draw
31
31
  print("<a href=\"#\" onclick=\"window.parent.location.href = " \
32
- "window.parent.location.href.split('#')[0] + " \
32
+ "window.parent.location.href.split('#')[0] + " \
33
33
  "'#element_#{name}'\">")
34
34
 
35
35
  process_description
data/lib/xsdvi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Xsdvi
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
@@ -16,6 +16,13 @@ module Xsdvi
16
16
  @root_node_name = nil
17
17
  @one_node_only = false
18
18
  @schema_namespace = nil
19
+
20
+ # Type registries for resolution (Phase 1)
21
+ @complex_types = {} # QName => complexType node
22
+ @simple_types = {} # QName => simpleType node
23
+ @model_groups = {} # QName => group node
24
+ @attribute_groups = {} # QName => attributeGroup node
25
+ @elements = {} # QName => element node (for refs)
19
26
  end
20
27
 
21
28
  def process_file(file_path)
@@ -30,12 +37,19 @@ module Xsdvi
30
37
  schema_node = doc.at_xpath("/xs:schema", "xs" => XSD_NAMESPACE)
31
38
  @schema_namespace = schema_node["targetNamespace"] if schema_node
32
39
 
40
+ # Phase 1: Collect all type definitions, groups, and elements
41
+ collect_type_definitions(doc)
42
+ collect_group_definitions(doc)
43
+ collect_attribute_group_definitions(doc)
44
+ collect_element_definitions(doc)
45
+
33
46
  # Create schema symbol when no root node specified (matches Java line 65-68)
34
47
  if root_node_name.nil?
35
48
  symbol = SVG::Symbols::Schema.new
36
49
  builder.set_root(symbol)
37
50
  end
38
51
 
52
+ # Phase 2: Process elements with type resolution
39
53
  process_element_declarations(doc)
40
54
 
41
55
  # Level up after processing all elements (matches Java line 71-73)
@@ -61,6 +75,42 @@ module Xsdvi
61
75
 
62
76
  private
63
77
 
78
+ def collect_type_definitions(doc)
79
+ # Collect complexType definitions
80
+ doc.xpath("//xs:complexType[@name]", "xs" => XSD_NAMESPACE).each do |node|
81
+ name = node["name"]
82
+ @complex_types[name] = node
83
+ end
84
+
85
+ # Collect simpleType definitions
86
+ doc.xpath("//xs:simpleType[@name]", "xs" => XSD_NAMESPACE).each do |node|
87
+ name = node["name"]
88
+ @simple_types[name] = node
89
+ end
90
+ end
91
+
92
+ def collect_group_definitions(doc)
93
+ doc.xpath("//xs:group[@name]", "xs" => XSD_NAMESPACE).each do |node|
94
+ name = node["name"]
95
+ @model_groups[name] = node
96
+ end
97
+ end
98
+
99
+ def collect_attribute_group_definitions(doc)
100
+ doc.xpath("//xs:attributeGroup[@name]",
101
+ "xs" => XSD_NAMESPACE).each do |node|
102
+ name = node["name"]
103
+ @attribute_groups[name] = node
104
+ end
105
+ end
106
+
107
+ def collect_element_definitions(doc)
108
+ doc.xpath("//xs:element[@name]", "xs" => XSD_NAMESPACE).each do |node|
109
+ name = node["name"]
110
+ @elements[name] = node
111
+ end
112
+ end
113
+
64
114
  def process_element_declarations(doc)
65
115
  elements = doc.xpath("//xs:schema/xs:element", "xs" => XSD_NAMESPACE)
66
116
 
@@ -75,9 +125,14 @@ module Xsdvi
75
125
  end
76
126
 
77
127
  def process_element_declaration(elem_node, cardinality, is_root)
128
+ # Phase 5: Handle element references first
129
+ if (ref = elem_node["ref"])
130
+ process_element_ref(ref, cardinality)
131
+ return
132
+ end
133
+
78
134
  name = elem_node["name"]
79
135
  elem_namespace = elem_node["namespace"]
80
- elem_node["ref"]
81
136
  type_attr = elem_node["type"]
82
137
  nillable = elem_node["nillable"] == "true"
83
138
  abstract = elem_node["abstract"] == "true"
@@ -118,20 +173,37 @@ module Xsdvi
118
173
 
119
174
  # Skip processing children if stack size > 1 and oneNodeOnly (matches Java line 253-260)
120
175
  unless @stack.size > 1 && one_node_only
121
- # Check if element has inline complexType
176
+ # Check for inline complexType first
122
177
  complex_type = elem_node.at_xpath("xs:complexType",
123
178
  "xs" => XSD_NAMESPACE)
124
179
  if complex_type
180
+ # Process inline anonymous complexType
125
181
  process_complex_type_node(complex_type)
126
- elsif !type_attr
182
+ elsif type_attr
183
+ # Try to resolve type reference
184
+ resolved_type = resolve_type(type_attr)
185
+ if resolved_type
186
+ # Process the resolved named type
187
+ if resolved_type.name == "complexType"
188
+ process_complex_type_node(resolved_type)
189
+ elsif resolved_type.name == "simpleType"
190
+ # simpleTypes don't have children to process
191
+ # The type string is already set above
192
+ end
193
+ elsif type_attr == "anyType"
194
+ # Explicit anyType reference
195
+ process_any_type_default
196
+ end
197
+ # If type is built-in (string, int, etc), we just show the type string
198
+ else
127
199
  # No type means anyType - expand it
128
200
  process_any_type_default
129
- elsif type_attr == "anyType"
130
- process_any_type_default
131
201
  end
132
202
  end
133
203
 
134
204
  @stack.pop
205
+ # Process identity constraints (matches Java line 262)
206
+ process_identity_constraints(elem_node)
135
207
  builder.level_up
136
208
  end
137
209
 
@@ -177,25 +249,102 @@ module Xsdvi
177
249
  end
178
250
 
179
251
  def process_complex_type_node(complex_type_node)
252
+ # Phase 6: Check for complexContent or simpleContent first
253
+ complex_content = complex_type_node.at_xpath("xs:complexContent",
254
+ "xs" => XSD_NAMESPACE)
255
+ simple_content = complex_type_node.at_xpath("xs:simpleContent",
256
+ "xs" => XSD_NAMESPACE)
257
+
258
+ if complex_content
259
+ process_complex_content(complex_content)
260
+ elsif simple_content
261
+ process_simple_content(simple_content)
262
+ else
263
+ # Direct content model (no extension/restriction wrapper)
264
+ process_complex_type_content(complex_type_node)
265
+ end
266
+ end
267
+
268
+ # Phase 6: Process complexContent
269
+ def process_complex_content(complex_content_node)
270
+ extension = complex_content_node.at_xpath("xs:extension",
271
+ "xs" => XSD_NAMESPACE)
272
+ restriction = complex_content_node.at_xpath("xs:restriction",
273
+ "xs" => XSD_NAMESPACE)
274
+
275
+ if extension
276
+ process_extension(extension)
277
+ elsif restriction
278
+ process_restriction(restriction)
279
+ end
280
+ end
281
+
282
+ # Phase 6: Process simpleContent
283
+ def process_simple_content(simple_content_node)
284
+ extension = simple_content_node.at_xpath("xs:extension",
285
+ "xs" => XSD_NAMESPACE)
286
+ restriction = simple_content_node.at_xpath("xs:restriction",
287
+ "xs" => XSD_NAMESPACE)
288
+
289
+ if extension
290
+ process_extension(extension)
291
+ elsif restriction
292
+ process_restriction(restriction)
293
+ end
294
+ end
295
+
296
+ # Phase 6: Process extension
297
+ def process_extension(extension_node)
298
+ # Process base type first (inherit content model)
299
+ if (base = extension_node["base"])
300
+ base_type = resolve_type(base)
301
+ process_complex_type_node(base_type) if base_type && base_type.name == "complexType"
302
+ end
303
+
304
+ # Then process extension's own content
305
+ process_complex_type_content(extension_node)
306
+ end
307
+
308
+ # Phase 6: Process restriction
309
+ def process_restriction(restriction_node)
310
+ # Process base type first (inherit and potentially restrict content model)
311
+ if (base = restriction_node["base"])
312
+ base_type = resolve_type(base)
313
+ process_complex_type_node(base_type) if base_type && base_type.name == "complexType"
314
+ end
315
+
316
+ # Then process restriction's own content
317
+ process_complex_type_content(restriction_node)
318
+ end
319
+
320
+ # Phase 6: Process complex type content (particles and attributes)
321
+ def process_complex_type_content(node)
180
322
  # Process particles (sequence/choice/all)
181
- sequence = complex_type_node.at_xpath("xs:sequence",
182
- "xs" => XSD_NAMESPACE)
323
+ sequence = node.at_xpath("xs:sequence", "xs" => XSD_NAMESPACE)
183
324
  process_sequence(sequence, nil) if sequence
184
325
 
185
- choice = complex_type_node.at_xpath("xs:choice", "xs" => XSD_NAMESPACE)
326
+ choice = node.at_xpath("xs:choice", "xs" => XSD_NAMESPACE)
186
327
  process_choice(choice, nil) if choice
187
328
 
188
- all_node = complex_type_node.at_xpath("xs:all", "xs" => XSD_NAMESPACE)
329
+ all_node = node.at_xpath("xs:all", "xs" => XSD_NAMESPACE)
189
330
  process_all(all_node, nil) if all_node
190
331
 
191
- # Process attributes
192
- attributes = complex_type_node.xpath("xs:attribute",
193
- "xs" => XSD_NAMESPACE)
194
- attributes.each { |attr| process_attribute(attr) }
332
+ # Process attributes (both direct and references) - SORT ALPHABETICALLY
333
+ attributes = node.xpath("xs:attribute", "xs" => XSD_NAMESPACE)
334
+ sorted_attributes = attributes.sort_by do |attr|
335
+ # Sort by ref or name
336
+ attr["ref"] || attr["name"] || ""
337
+ end
338
+ sorted_attributes.each { |attr| process_attribute(attr) }
339
+
340
+ # Process attribute group references
341
+ attr_groups = node.xpath("xs:attributeGroup[@ref]", "xs" => XSD_NAMESPACE)
342
+ attr_groups.each do |attr_group|
343
+ process_attribute_group_ref(attr_group["ref"])
344
+ end
195
345
 
196
346
  # Process attribute wildcard
197
- any_attr = complex_type_node.at_xpath("xs:anyAttribute",
198
- "xs" => XSD_NAMESPACE)
347
+ any_attr = node.at_xpath("xs:anyAttribute", "xs" => XSD_NAMESPACE)
199
348
  process_any_attribute(any_attr) if any_attr
200
349
  end
201
350
 
@@ -211,6 +360,12 @@ module Xsdvi
211
360
  process_element_declaration(elem, get_cardinality(elem), false)
212
361
  end
213
362
 
363
+ # Process group references (Phase 3)
364
+ sequence_node.xpath("xs:group[@ref]",
365
+ "xs" => XSD_NAMESPACE).each do |group|
366
+ process_group_ref(group["ref"])
367
+ end
368
+
214
369
  # Process any
215
370
  sequence_node.xpath("xs:any", "xs" => XSD_NAMESPACE).each do |any|
216
371
  process_any(any)
@@ -231,6 +386,11 @@ module Xsdvi
231
386
  process_element_declaration(elem, get_cardinality(elem), false)
232
387
  end
233
388
 
389
+ # Process group references (Phase 3)
390
+ choice_node.xpath("xs:group[@ref]", "xs" => XSD_NAMESPACE).each do |group|
391
+ process_group_ref(group["ref"])
392
+ end
393
+
234
394
  builder.level_up
235
395
  end
236
396
 
@@ -246,6 +406,11 @@ module Xsdvi
246
406
  process_element_declaration(elem, get_cardinality(elem), false)
247
407
  end
248
408
 
409
+ # Process group references (Phase 3)
410
+ all_node.xpath("xs:group[@ref]", "xs" => XSD_NAMESPACE).each do |group|
411
+ process_group_ref(group["ref"])
412
+ end
413
+
249
414
  builder.level_up
250
415
  end
251
416
 
@@ -258,7 +423,7 @@ module Xsdvi
258
423
  when "skip" then SVG::Symbol::PC_SKIP
259
424
  when "lax" then SVG::Symbol::PC_LAX
260
425
  when "strict" then SVG::Symbol::PC_STRICT
261
- else SVG::Symbol::PC_LAX # Default is lax for anyType
426
+ else SVG::Symbol::PC_LAX # Default is lax for anyType
262
427
  end
263
428
  symbol.cardinality = get_cardinality(any_node)
264
429
  symbol.description = extract_documentation(any_node)
@@ -267,18 +432,123 @@ module Xsdvi
267
432
  end
268
433
 
269
434
  def process_attribute(attr_node)
435
+ # Handle attribute references (like <xsd:attribute ref="xml:lang"/>)
436
+ if (ref = attr_node["ref"])
437
+ process_attribute_ref(ref, attr_node)
438
+ return
439
+ end
440
+
270
441
  symbol = SVG::Symbols::Attribute.new
271
442
  symbol.name = attr_node["name"]
272
443
  namespace = attr_node["namespace"]
273
444
  symbol.namespace = namespace if namespace && namespace != schema_namespace
274
- symbol.type = attr_node["type"] ? "type: #{attr_node['type']}" : nil
445
+
446
+ # Determine type - either from type attribute or inline simpleType restriction
447
+ type_value = nil
448
+ type_prefix = "type"
449
+
450
+ if attr_node["type"]
451
+ type_value = attr_node["type"]
452
+ if type_value.start_with?("xsd:")
453
+ type_value = type_value.sub(/^xsd:/,
454
+ "")
455
+ end
456
+ else
457
+ # Check for inline simpleType definition
458
+ simple_type = attr_node.at_xpath("xs:simpleType", "xs" => XSD_NAMESPACE)
459
+ if simple_type
460
+ # Check for union type first
461
+ union = simple_type.at_xpath("xs:union", "xs" => XSD_NAMESPACE)
462
+ if union
463
+ # Union types have base type of anySimpleType
464
+ type_value = "anySimpleType"
465
+ type_prefix = "base"
466
+ else
467
+ # Check for restriction
468
+ restriction = simple_type.at_xpath("xs:restriction",
469
+ "xs" => XSD_NAMESPACE)
470
+ if restriction && restriction["base"]
471
+ type_value = restriction["base"]
472
+ if type_value.start_with?("xsd:")
473
+ type_value = type_value.sub(/^xsd:/,
474
+ "")
475
+ end
476
+ type_prefix = "base"
477
+ end
478
+ end
479
+ end
480
+ end
481
+
482
+ symbol.type = "#{type_prefix}: #{type_value}" if type_value
483
+
275
484
  symbol.required = attr_node["use"] == "required"
485
+
486
+ # Capture default or fixed values
487
+ if attr_node["default"]
488
+ default_value = attr_node["default"]
489
+
490
+ # Format default value for double type: 0 becomes 0.0E1
491
+ if type_value == "double" && (default_value == "0" || default_value.to_f == 0.0)
492
+ default_value = "0.0E1"
493
+ end
494
+
495
+ symbol.constraint = "default: #{default_value}"
496
+ elsif attr_node["fixed"]
497
+ symbol.constraint = "fixed: #{attr_node['fixed']}"
498
+ end
499
+
276
500
  symbol.description = extract_documentation(attr_node)
277
501
 
278
502
  builder.append_child(symbol)
279
503
  builder.level_up
280
504
  end
281
505
 
506
+ # Process attribute reference
507
+ def process_attribute_ref(ref, ref_node)
508
+ # Extract namespace prefix and local name
509
+ if ref.include?(":")
510
+ prefix, local_name = ref.split(":", 2)
511
+ else
512
+ prefix = nil
513
+ local_name = ref
514
+ end
515
+
516
+ symbol = SVG::Symbols::Attribute.new
517
+
518
+ # Handle xml: namespace attributes specially
519
+ if prefix == "xml"
520
+ # W3C XML namespace
521
+ symbol.namespace = "http://www.w3.org/XML/1998/namespace"
522
+ symbol.name = local_name # Just "lang" or "id", not "xml:lang" or "xml:id"
523
+
524
+ # Set type based on specific xml: attribute
525
+ symbol.type = case local_name
526
+ when "id"
527
+ "type: ID"
528
+ when "lang"
529
+ "base: anySimpleType"
530
+ when "space"
531
+ "type: NCName"
532
+ when "base"
533
+ "type: anyURI"
534
+ else
535
+ "base: anySimpleType"
536
+ end
537
+ else
538
+ # Regular attribute reference - would need to look up in schema
539
+ symbol.name = local_name
540
+ end
541
+
542
+ # Get use constraint from the reference location
543
+ symbol.required = ref_node["use"] == "required"
544
+
545
+ # Extract documentation from the reference node itself
546
+ symbol.description = extract_documentation(ref_node)
547
+
548
+ builder.append_child(symbol)
549
+ builder.level_up
550
+ end
551
+
282
552
  def process_any_attribute(any_attr_node)
283
553
  symbol = SVG::Symbols::AnyAttribute.new
284
554
  namespace = any_attr_node["namespace"]
@@ -288,36 +558,201 @@ module Xsdvi
288
558
  when "skip" then SVG::Symbol::PC_SKIP
289
559
  when "lax" then SVG::Symbol::PC_LAX
290
560
  when "strict" then SVG::Symbol::PC_STRICT
291
- else SVG::Symbol::PC_LAX # Default is lax for anyType
561
+ else SVG::Symbol::PC_LAX # Default is lax for anyType
292
562
  end
293
563
  symbol.description = extract_documentation(any_attr_node)
294
564
  builder.append_child(symbol)
295
565
  builder.level_up
296
566
  end
297
567
 
568
+ # Phase 3: Group reference resolution
569
+ def process_group_ref(ref)
570
+ return unless ref
571
+
572
+ # Strip namespace prefix if present
573
+ group_name = ref.include?(":") ? ref.split(":").last : ref
574
+ group_node = @model_groups[group_name]
575
+
576
+ return unless group_node
577
+
578
+ # Process the referenced group's content (sequence, choice, or all)
579
+ model_group = group_node.at_xpath("xs:sequence | xs:choice | xs:all",
580
+ "xs" => XSD_NAMESPACE)
581
+ return unless model_group
582
+
583
+ case model_group.name
584
+ when "sequence"
585
+ process_sequence(model_group, nil)
586
+ when "choice"
587
+ process_choice(model_group, nil)
588
+ when "all"
589
+ process_all(model_group, nil)
590
+ end
591
+ end
592
+
593
+ # Phase 4: Attribute group reference resolution
594
+ def process_attribute_group_ref(ref)
595
+ return unless ref
596
+
597
+ # Strip namespace prefix if present
598
+ group_name = ref.include?(":") ? ref.split(":").last : ref
599
+ group_node = @attribute_groups[group_name]
600
+
601
+ return unless group_node
602
+
603
+ # Process all attributes in the group
604
+ group_node.xpath("xs:attribute", "xs" => XSD_NAMESPACE).each do |attr|
605
+ process_attribute(attr)
606
+ end
607
+
608
+ # Process nested attribute group references (can be recursive)
609
+ group_node.xpath("xs:attributeGroup[@ref]",
610
+ "xs" => XSD_NAMESPACE).each do |nested|
611
+ process_attribute_group_ref(nested["ref"])
612
+ end
613
+ end
614
+
615
+ # Phase 5: Element reference resolution
616
+ def process_element_ref(ref, cardinality)
617
+ return unless ref
618
+
619
+ # Strip namespace prefix if present
620
+ elem_name = ref.include?(":") ? ref.split(":").last : ref
621
+ elem_node = @elements[elem_name]
622
+
623
+ return unless elem_node
624
+
625
+ # Check for circular reference to prevent infinite recursion
626
+ if @stack.any? { |e| e["name"] == elem_name }
627
+ # Create a loop symbol instead (Loop doesn't have a name attribute)
628
+ symbol = SVG::Symbols::Loop.new
629
+ builder.append_child(symbol)
630
+ builder.level_up
631
+ return
632
+ end
633
+
634
+ # Process the referenced element
635
+ process_element_declaration(elem_node, cardinality, false)
636
+ end
637
+
298
638
  def process_loop?(elem_node)
299
639
  @stack.any?(elem_node)
300
640
  end
301
641
 
302
642
  def get_cardinality(node)
303
643
  min_occurs = node["minOccurs"]&.to_i || 1
304
- max_occurs = node["maxOccurs"]
644
+ max_occurs_str = node["maxOccurs"] || "1" # XSD default is "1" when not specified
305
645
 
306
- return nil if min_occurs == 1 && (max_occurs.nil? || max_occurs == "1")
646
+ return nil if min_occurs == 1 && max_occurs_str == "1"
307
647
 
308
- if max_occurs == "unbounded"
648
+ if max_occurs_str == "unbounded"
309
649
  "#{min_occurs}..∞"
310
- elsif max_occurs
650
+ else
651
+ max_occurs = max_occurs_str.to_i
311
652
  "#{min_occurs}..#{max_occurs}"
312
653
  end
313
654
  end
314
655
 
315
656
  def extract_documentation(node)
316
657
  docs = node.xpath(
317
- ".//xs:annotation/xs:documentation",
658
+ "./xs:annotation/xs:documentation", # Changed from .// to ./ (direct children only)
318
659
  "xs" => XSD_NAMESPACE,
319
660
  )
320
- docs.map(&:text).map { |text| text.gsub(/\n[ \t]+/, "\n") }
661
+ # Use inner_html to preserve XML entities like &lt; and &gt;
662
+ # Java's XML parser preserves these, affecting wrap length calculations
663
+ docs.map(&:inner_html).map { |text| text.gsub(/\n[ \t]+/, "\n") }
664
+ end
665
+
666
+ # Phase 2: Type resolution methods
667
+ def resolve_type(type_attr)
668
+ return nil unless type_attr
669
+
670
+ # Strip namespace prefix if present (e.g., "xs:string" -> "string")
671
+ type_name = type_attr.include?(":") ? type_attr.split(":").last : type_attr
672
+
673
+ # Don't try to resolve built-in XSD types
674
+ return nil if is_builtin_type?(type_name)
675
+
676
+ # Look up in registries
677
+ @complex_types[type_name] || @simple_types[type_name]
678
+ end
679
+
680
+ def is_builtin_type?(type_name)
681
+ # W3C XML Schema built-in types
682
+ %w[
683
+ string boolean decimal float double duration dateTime
684
+ time date gYearMonth gYear gMonthDay gDay gMonth
685
+ hexBinary base64Binary anyURI QName NOTATION
686
+ normalizedString token language NMTOKEN NMTOKENS
687
+ Name NCName ID IDREF IDREFS ENTITY ENTITIES
688
+ integer nonPositiveInteger negativeInteger long int
689
+ short byte nonNegativeInteger unsignedLong unsignedInt
690
+ unsignedShort unsignedByte positiveInteger
691
+ anyType anySimpleType anyAtomicType
692
+ ].include?(type_name)
693
+ end
694
+
695
+ def process_identity_constraints(elem_node)
696
+ # Process xs:key
697
+ elem_node.xpath("xs:key", "xs" => XSD_NAMESPACE).each do |constraint|
698
+ process_identity_constraint(constraint, :key)
699
+ end
700
+
701
+ # Process xs:keyref
702
+ elem_node.xpath("xs:keyref", "xs" => XSD_NAMESPACE).each do |constraint|
703
+ process_identity_constraint(constraint, :keyref)
704
+ end
705
+
706
+ # Process xs:unique
707
+ elem_node.xpath("xs:unique", "xs" => XSD_NAMESPACE).each do |constraint|
708
+ process_identity_constraint(constraint, :unique)
709
+ end
710
+ end
711
+
712
+ def process_identity_constraint(constraint_node, category)
713
+ # Create appropriate symbol based on category
714
+ symbol = case category
715
+ when :key
716
+ SVG::Symbols::Key.new
717
+ when :keyref
718
+ SVG::Symbols::Keyref.new
719
+ when :unique
720
+ SVG::Symbols::Unique.new
721
+ end
722
+
723
+ # Set common properties
724
+ symbol.name = constraint_node["name"]
725
+ namespace = constraint_node["namespace"]
726
+ symbol.namespace = namespace if namespace && namespace != schema_namespace
727
+ symbol.description = extract_documentation(constraint_node)
728
+
729
+ # For keyref, set the refer attribute
730
+ if category == :keyref
731
+ symbol.refer = constraint_node["refer"]
732
+ end
733
+
734
+ builder.append_child(symbol)
735
+
736
+ # Process selector
737
+ selector_node = constraint_node.at_xpath("xs:selector",
738
+ "xs" => XSD_NAMESPACE)
739
+ if selector_node
740
+ selector_symbol = SVG::Symbols::Selector.new
741
+ selector_symbol.xpath = selector_node["xpath"]
742
+ builder.append_child(selector_symbol)
743
+ builder.level_up
744
+ end
745
+
746
+ # Process field(s)
747
+ constraint_node.xpath("xs:field",
748
+ "xs" => XSD_NAMESPACE).each do |field_node|
749
+ field_symbol = SVG::Symbols::Field.new
750
+ field_symbol.xpath = field_node["xpath"]
751
+ builder.append_child(field_symbol)
752
+ builder.level_up
753
+ end
754
+
755
+ builder.level_up
321
756
  end
322
757
  end
323
758
  end
data/lib/xsdvi.rb CHANGED
@@ -25,6 +25,12 @@ require_relative "xsdvi/utils/writer"
25
25
  require_relative "xsdvi/utils/resource_loader"
26
26
  require_relative "xsdvi/utils/width_calculator"
27
27
 
28
+ # Comparison components
29
+ require_relative "xsdvi/comparison/java_manager"
30
+ require_relative "xsdvi/comparison/metadata_extractor"
31
+ require_relative "xsdvi/comparison/html_generator"
32
+ require_relative "xsdvi/comparison/dual_generator"
33
+
28
34
  module Xsdvi
29
35
  class Error < StandardError; end
30
36
  end