xseed 1.0.0

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rake.yml +16 -0
  3. data/.github/workflows/release.yml +25 -0
  4. data/.gitignore +72 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +11 -0
  7. data/.rubocop_todo.yml +432 -0
  8. data/CHANGELOG.adoc +446 -0
  9. data/Gemfile +21 -0
  10. data/LICENSE.adoc +29 -0
  11. data/README.adoc +386 -0
  12. data/Rakefile +11 -0
  13. data/examples/README.adoc +334 -0
  14. data/examples/advanced_usage.rb +286 -0
  15. data/examples/html_generation.rb +167 -0
  16. data/examples/parser_usage.rb +102 -0
  17. data/examples/schemas/person.xsd +171 -0
  18. data/examples/simple_generation.rb +149 -0
  19. data/exe/xseed +6 -0
  20. data/lib/xseed/cli.rb +376 -0
  21. data/lib/xseed/documentation/config.rb +101 -0
  22. data/lib/xseed/documentation/constants.rb +76 -0
  23. data/lib/xseed/documentation/generators/hierarchy_table_generator.rb +554 -0
  24. data/lib/xseed/documentation/generators/instance_sample_generator.rb +723 -0
  25. data/lib/xseed/documentation/generators/properties_table_generator.rb +983 -0
  26. data/lib/xseed/documentation/html_generator.rb +836 -0
  27. data/lib/xseed/documentation/html_generator.rb.bak +723 -0
  28. data/lib/xseed/documentation/presentation/css_generator.rb +510 -0
  29. data/lib/xseed/documentation/presentation/javascript_generator.rb +151 -0
  30. data/lib/xseed/documentation/presentation/navigation_builder.rb +169 -0
  31. data/lib/xseed/documentation/schema_loader.rb +121 -0
  32. data/lib/xseed/documentation/utils/helpers.rb +205 -0
  33. data/lib/xseed/documentation/utils/namespaces.rb +149 -0
  34. data/lib/xseed/documentation/utils/references.rb +135 -0
  35. data/lib/xseed/documentation/utils/strings.rb +75 -0
  36. data/lib/xseed/models/element_declaration.rb +144 -0
  37. data/lib/xseed/parser/xsd_parser.rb +192 -0
  38. data/lib/xseed/version.rb +5 -0
  39. data/lib/xseed.rb +76 -0
  40. data/xseed.gemspec +39 -0
  41. metadata +158 -0
@@ -0,0 +1,723 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require_relative "config"
5
+ require_relative "constants"
6
+ require_relative "utils/references"
7
+ require_relative "utils/namespaces"
8
+ require_relative "utils/helpers"
9
+ require_relative "generators/properties_table_generator"
10
+ require_relative "generators/hierarchy_table_generator"
11
+ require_relative "generators/instance_sample_generator"
12
+ require_relative "presentation/css_generator"
13
+ require_relative "presentation/javascript_generator"
14
+ require_relative "presentation/navigation_builder"
15
+ require_relative "../parser/xsd_parser"
16
+
17
+ module Xseed
18
+ module Documentation
19
+ # Main HTML documentation generator
20
+ # Integrates all content generators for complete schema documentation
21
+ class HtmlGenerator
22
+ include Utils::References
23
+ include Utils::Namespaces
24
+ include Utils::Helpers
25
+ include Constants
26
+
27
+ attr_reader :xsd_file, :config, :parser
28
+
29
+ # Initialize the HTML generator
30
+ #
31
+ # @param xsd_file [String] Path to XSD schema file
32
+ # @param config [Config] Configuration options (optional)
33
+ def initialize(xsd_file, config = Config.new)
34
+ @xsd_file = xsd_file
35
+ @config = config
36
+ @parser = Parser::XsdParser.new(xsd_file)
37
+ end
38
+
39
+ # Generate HTML documentation
40
+ #
41
+ # @return [String] Generated HTML content
42
+ def generate
43
+ builder = Nokogiri::HTML::Builder.new do |html|
44
+ html.html(lang: "en") do
45
+ generate_head(html)
46
+ html.body do
47
+ generate_body(html)
48
+ generate_navigation(html)
49
+ end
50
+ end
51
+ end
52
+ builder.to_html
53
+ end
54
+
55
+ # Generate HTML documentation and write to file
56
+ #
57
+ # @param output_path [String] Path to output HTML file
58
+ def generate_file(output_path)
59
+ html_content = generate
60
+ File.write(output_path, html_content)
61
+ end
62
+
63
+ private
64
+
65
+ # Generate HTML head section
66
+ #
67
+ # @param html [Nokogiri::HTML::Builder] HTML builder
68
+ def generate_head(html)
69
+ html.head do
70
+ html.meta(charset: "UTF-8")
71
+ html.meta(name: "viewport",
72
+ content: "width=device-width, initial-scale=1.0")
73
+ html.title(title)
74
+ generate_styles(html)
75
+ generate_scripts(html)
76
+ end
77
+ end
78
+
79
+ # Generate styles (inline or external)
80
+ #
81
+ # @param html [Nokogiri::HTML::Builder] HTML builder
82
+ def generate_styles(html)
83
+ css_gen = Presentation::CssGenerator.new(@config)
84
+
85
+ if css_gen.external_css_url
86
+ html.link(rel: "stylesheet", href: css_gen.external_css_url)
87
+ else
88
+ html.style do
89
+ html.text(css_gen.generate)
90
+ end
91
+ end
92
+ end
93
+
94
+ # Generate scripts (inline or external)
95
+ #
96
+ # @param html [Nokogiri::HTML::Builder] HTML builder
97
+ def generate_scripts(html)
98
+ js_gen = Presentation::JavascriptGenerator.new(@config)
99
+
100
+ # jQuery
101
+ html.script(src: js_gen.jquery_url, defer: true) {}
102
+
103
+ # Bootstrap JS (if enabled)
104
+ if js_gen.bootstrap_url
105
+ html.script(src: "#{js_gen.bootstrap_url}/js/bootstrap.min.js",
106
+ defer: true) {}
107
+ end
108
+
109
+ # Custom JavaScript
110
+ html.script(defer: true) do
111
+ html.text(js_gen.generate)
112
+ end
113
+ end
114
+
115
+ # Generate navigation sidebar
116
+ #
117
+ # @param html [Nokogiri::HTML::Builder] HTML builder
118
+ def generate_navigation(html)
119
+ nav_builder = Presentation::NavigationBuilder.new(@parser, @config)
120
+ html << nav_builder.generate
121
+ end
122
+
123
+
124
+ # Generate HTML body content
125
+ #
126
+ # @param html [Nokogiri::HTML::Builder] HTML builder
127
+ def generate_body(html)
128
+ # Toggle button as direct child of body
129
+ html.div(id: "toggle") do
130
+ html.span "<"
131
+ end
132
+
133
+ html.main do
134
+ html.div(class: "title-section") do
135
+ html.h1 do
136
+ html.a(id: "top") {}
137
+ html.text title
138
+ end
139
+ end
140
+
141
+ # Schema-level information
142
+ generate_schema_info(html)
143
+
144
+ # Generate documentation for each component type
145
+ if @config.sort_by_component
146
+ generate_sorted_components(html)
147
+ else
148
+ generate_unsorted_components(html)
149
+ end
150
+
151
+ # Glossary section (if enabled)
152
+ generate_glossary(html) if @config.print_glossary
153
+ end
154
+ end
155
+
156
+ # Generate components in sorted order
157
+ #
158
+ # @param html [Nokogiri::HTML::Builder] HTML builder
159
+ def generate_sorted_components(html)
160
+ generate_elements_section(html) if parser.elements.any?
161
+ generate_complex_types_section(html) if parser.complex_types.any?
162
+ generate_simple_types_section(html) if parser.simple_types.any?
163
+ generate_groups_section(html) if parser.groups.any?
164
+ generate_attribute_groups_section(html) if parser.attribute_groups.any?
165
+ end
166
+
167
+ # Generate components in unsorted order
168
+ #
169
+ # @param html [Nokogiri::HTML::Builder] HTML builder
170
+ def generate_unsorted_components(html)
171
+ html.section(id: "SchemaComponents") do
172
+ html.h2 "Global Schema Components"
173
+
174
+ # Generate all components in order they appear
175
+ all_components = []
176
+ if parser.attribute_groups.any?
177
+ all_components.concat(parser.attribute_groups)
178
+ end
179
+ if parser.complex_types.any?
180
+ all_components.concat(parser.complex_types)
181
+ end
182
+ all_components.concat(parser.elements) if parser.elements.any?
183
+ all_components.concat(parser.groups) if parser.groups.any?
184
+ all_components.concat(parser.simple_types) if parser.simple_types.any?
185
+
186
+ all_components.each do |component|
187
+ component_type = get_component_type_label(component.name)
188
+ generate_component_section(html, component, component_type)
189
+ end
190
+ end
191
+ end
192
+
193
+ # Get component type label
194
+ #
195
+ # @param node_name [String] XML node name
196
+ # @return [String] Human-readable component type
197
+ def get_component_type_label(node_name)
198
+ case node_name
199
+ when "element"
200
+ "Element"
201
+ when "complexType"
202
+ "Complex Type"
203
+ when "simpleType"
204
+ "Simple Type"
205
+ when "group"
206
+ "Model Group"
207
+ when "attributeGroup"
208
+ "Attribute Group"
209
+ when "attribute"
210
+ "Attribute"
211
+ when "notation"
212
+ "Notation"
213
+ else
214
+ node_name
215
+ end
216
+ end
217
+
218
+ # Generate glossary section
219
+ #
220
+ # @param html [Nokogiri::HTML::Builder] HTML builder
221
+ def generate_glossary(html)
222
+ html.section(id: "Glossary") do
223
+ html.h2 "Glossary"
224
+ html.p "XSD schema terms and definitions."
225
+ # TODO: Implement glossary generation
226
+ end
227
+ end
228
+
229
+ # Generate schema information section
230
+ #
231
+ # @param html [Nokogiri::HTML::Builder] HTML builder
232
+ def generate_schema_info(html)
233
+ schema = parser.schema
234
+ return unless schema
235
+
236
+ html.section(id: "SectionSchemaProperties", class: "schema-info") do
237
+ html.h2 do
238
+ html.a(id: "SchemaProperties") {}
239
+ html.text "Schema Document Properties"
240
+ end
241
+
242
+ html.dl(class: "dl-horizontal") do
243
+ # Target Namespace
244
+ html.dt(class: "header") do
245
+ html.a(title: "Look up 'Target Namespace' in glossary",
246
+ href: "#term_TargetNS") do
247
+ html.text "Target Namespace"
248
+ end
249
+ end
250
+ html.dd(class: "") do
251
+ if (target_ns = schema["targetNamespace"])
252
+ html.text target_ns
253
+ else
254
+ html.text "None"
255
+ end
256
+ end
257
+
258
+ # Element and Attribute Namespaces
259
+ html.dt(class: "header") { html.text "Element and Attribute Namespaces" }
260
+ html.dd(class: "") do
261
+ html.ul do
262
+ html.li do
263
+ html.text "Global element and attribute declarations belong to this schema's target namespace."
264
+ end
265
+ html.li do
266
+ html.text "By default, local element declarations have no namespace."
267
+ end
268
+ html.li do
269
+ html.text "By default, local attribute declarations have no namespace."
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ # Declared Namespaces
276
+ generate_declared_namespaces(html, schema)
277
+
278
+ # Schema Component Representation
279
+ generate_schema_component_callout(html, schema)
280
+
281
+ html.div(style: "text-align: right; clear: both;") do
282
+ html.a(href: "#top", title: "Go to top of page") do
283
+ html.span(class: "glyphicon glyphicon-chevron-up") { html.text " " }
284
+ end
285
+ end
286
+ html.hr
287
+ end
288
+ end
289
+
290
+ # Generate declared namespaces section
291
+ #
292
+ # @param html [Nokogiri::HTML::Builder] HTML builder
293
+ # @param schema [Nokogiri::XML::Element] Schema element
294
+ def generate_declared_namespaces(html, schema)
295
+ html.h4 "Declared Namespaces:"
296
+ html.dl(class: "dl-horizontal") do
297
+ html.dt(class: "header") { html.text "Prefix" }
298
+ html.dd(class: "header") { html.text "Namespace" }
299
+
300
+ # Extract namespace declarations
301
+ schema.namespace_definitions.each do |ns|
302
+ prefix = ns.prefix || "(default)"
303
+ html.dt(class: "") do
304
+ html.a(id: "ns_#{ns.prefix}") {} if ns.prefix
305
+ html.text prefix
306
+ end
307
+ html.dd(class: "") { html.text ns.href }
308
+ end
309
+ end
310
+ end
311
+
312
+ # Generate elements section
313
+ #
314
+ # @param html [Nokogiri::HTML::Builder] HTML builder
315
+ def generate_elements_section(html)
316
+ html.section(id: "SectionSchemaElements") do
317
+ html.h2 do
318
+ html.a(id: "SchemaElements") {}
319
+ html.text "Elements"
320
+ end
321
+
322
+ parser.elements.each do |element|
323
+ generate_component_section(html, element, "Element")
324
+ end
325
+ end
326
+ end
327
+
328
+ # Generate complex types section (kept for backwards compatibility)
329
+ #
330
+ # @param html [Nokogiri::HTML::Builder] HTML builder
331
+ def generate_complex_types_section(html)
332
+ html.section(id: "SectionSchemaComplexTypes") do
333
+ html.h2 do
334
+ html.a(id: "SchemaComplexTypes") {}
335
+ html.text "Complex Types"
336
+ end
337
+
338
+ parser.complex_types.each do |type|
339
+ generate_component_section(html, type, "Complex Type")
340
+ end
341
+ end
342
+ end
343
+
344
+ # Generate simple types section (kept for backwards compatibility)
345
+ #
346
+ # @param html [Nokogiri::HTML::Builder] HTML builder
347
+ def generate_simple_types_section(html)
348
+ html.section(id: "SectionSchemaSimpleTypes") do
349
+ html.h2 do
350
+ html.a(id: "SchemaSimpleTypes") {}
351
+ html.text "Simple Types"
352
+ end
353
+
354
+ parser.simple_types.each do |type|
355
+ generate_component_section(html, type, "Simple Type")
356
+ end
357
+ end
358
+ end
359
+ # Generate groups section
360
+ #
361
+ # @param html [Nokogiri::HTML::Builder] HTML builder
362
+ def generate_groups_section(html)
363
+ html.section(id: "SectionSchemaGroups") do
364
+ html.h2 do
365
+ html.a(id: "SchemaGroups") {}
366
+ html.text "Model Groups"
367
+ end
368
+
369
+ parser.groups.each do |group|
370
+ generate_component_section(html, group, "Model Group")
371
+ # Generate combined types section (complex and simple)
372
+ #
373
+ # @param html [Nokogiri::HTML::Builder] HTML builder
374
+ def generate_types_section(html)
375
+ html.section(id: "SectionSchemaTypes", class: "component-group") do
376
+ html.h2 do
377
+ html.a(id: "SchemaTypes") {}
378
+ html.text "Global Types"
379
+ end
380
+
381
+ # Complex types first
382
+ parser.complex_types.each do |type|
383
+ generate_component_div(html, type, "Complex Type")
384
+ end
385
+
386
+ # Then simple types
387
+ parser.simple_types.each do |type|
388
+ generate_component_div(html, type, "Simple Type")
389
+ end
390
+ end
391
+ end
392
+
393
+ # Generate combined groups and attribute groups section
394
+ #
395
+ # @param html [Nokogiri::HTML::Builder] HTML builder
396
+ def generate_groups_and_attributes_section(html)
397
+ html.section(id: "SectionSchemaGroups", class: "component-group") do
398
+ html.h2 do
399
+ html.a(id: "SchemaGroups") {}
400
+ html.text "Global Groups and Attributes"
401
+ end
402
+
403
+ # Model groups
404
+ parser.groups.each do |group|
405
+ generate_component_div(html, group, "Model Group")
406
+ end
407
+
408
+ # Attribute groups
409
+ parser.attribute_groups.each do |group|
410
+ generate_component_div(html, group, "Attribute Group")
411
+ end
412
+ end
413
+ end
414
+
415
+ # Generate component as div (not section) within a grouped section
416
+ #
417
+ # @param html [Nokogiri::HTML::Builder] HTML builder
418
+ # @param component [Nokogiri::XML::Element] Schema component
419
+ # @param component_type [String] Component type label
420
+ def generate_component_div(html, component, component_type)
421
+ component_name = component["name"] || component["ref"]
422
+ return unless component_name
423
+
424
+ component_id = generate_component_id(component_type, component_name)
425
+
426
+ html.div(id: component_id, class: "component") do
427
+ html.h3(class: "xs3p-subsection-heading") do
428
+ html.text "#{component_type}: "
429
+ html.strong component_name
430
+ end
431
+
432
+ # SVG diagram reference (if exists)
433
+ generate_svg_reference(html, component_name)
434
+
435
+ # Properties definition list (no heading)
436
+ props_gen = Generators::PropertiesTableGenerator.new(component,
437
+ @config)
438
+ html << props_gen.generate
439
+
440
+ # Hierarchy table (if applicable)
441
+ hier_gen = Generators::HierarchyTableGenerator.new(
442
+ component,
443
+ @parser,
444
+ @config
445
+ )
446
+ hierarchy_html = hier_gen.generate
447
+ html << hierarchy_html if hierarchy_html
448
+
449
+ # Instance sample in callout block (skip for simple types and notations)
450
+ unless %w[simpleType notation].include?(component.name)
451
+ generate_instance_representation_callout(html, component)
452
+ end
453
+
454
+ # Schema component representation in callout block
455
+ generate_schema_component_callout(html, component)
456
+
457
+ # Back to top link and separator
458
+ html.div(style: "text-align: right; clear: both;") do
459
+ html.a(href: "#top", title: "Go to top of page") do
460
+ html.span(class: "glyphicon glyphicon-chevron-up") { html.text " " }
461
+ end
462
+ end
463
+ html.hr
464
+ end
465
+ end
466
+
467
+ end
468
+ end
469
+ end
470
+
471
+ # Generate attribute groups section
472
+ #
473
+ # @param html [Nokogiri::HTML::Builder] HTML builder
474
+ def generate_attribute_groups_section(html)
475
+ html.section(id: "SectionSchemaAttributeGroups") do
476
+ html.h2 do
477
+ html.a(id: "SchemaAttributeGroups") {}
478
+ html.text "Attribute Groups"
479
+ end
480
+
481
+ parser.attribute_groups.each do |group|
482
+ generate_component_section(html, group, "Attribute Group")
483
+ end
484
+ end
485
+ end
486
+
487
+ # Generate component section with all generators
488
+ #
489
+ # @param html [Nokogiri::HTML::Builder] HTML builder
490
+ # @param component [Nokogiri::XML::Element] Schema component
491
+ # @param component_type [String] Component type label
492
+ def generate_component_section(html, component, component_type)
493
+ component_name = component["name"] || component["ref"]
494
+ return unless component_name
495
+
496
+ component_id = generate_component_id(component_type, component_name)
497
+
498
+ html.section(id: component_id) do
499
+ html.h3(class: "xs3p-subsection-heading") do
500
+ html.text "#{component_type}: "
501
+ html.strong component_name
502
+ end
503
+
504
+ # SVG diagram reference (if exists)
505
+ generate_svg_reference(html, component_name)
506
+
507
+ # Properties definition list (no heading)
508
+ props_gen = Generators::PropertiesTableGenerator.new(component,
509
+ @config)
510
+ html << props_gen.generate
511
+
512
+ # Hierarchy table (if applicable)
513
+ hier_gen = Generators::HierarchyTableGenerator.new(
514
+ component,
515
+ @parser,
516
+ @config
517
+ )
518
+ hierarchy_html = hier_gen.generate
519
+ html << hierarchy_html if hierarchy_html
520
+
521
+ # Instance sample in callout block (skip for simple types and notations)
522
+ unless %w[simpleType notation].include?(component.name)
523
+ generate_instance_representation_callout(html, component)
524
+ end
525
+
526
+ # Schema component representation in callout block
527
+ generate_schema_component_callout(html, component)
528
+
529
+ # Back to top link and separator
530
+ html.div(style: "text-align: right; clear: both;") do
531
+ html.a(href: "#top", title: "Go to top of page") do
532
+ html.span(class: "glyphicon glyphicon-chevron-up") { html.text " " }
533
+ end
534
+ end
535
+ html.hr
536
+ end
537
+ end
538
+
539
+ # Generate SVG diagram reference
540
+ #
541
+ # @param html [Nokogiri::HTML::Builder] HTML builder
542
+ # @param component_name [String] Component name
543
+ def generate_svg_reference(html, component_name)
544
+ return unless @config.print_diagrams
545
+
546
+ html.object(data: "diagrams/#{component_name}.svg",
547
+ type: "image/svg+xml") {}
548
+ end
549
+
550
+ # Generate instance representation callout block
551
+ #
552
+ # @param html [Nokogiri::HTML::Builder] HTML builder
553
+ # @param component [Nokogiri::XML::Element] Schema component
554
+ def generate_instance_representation_callout(html, component)
555
+ html.div(class: "bs-callout bs-callout-info") do
556
+ html.h4 do
557
+ html.text "XML Instance Representation"
558
+ html.text " "
559
+ generate_help_popover(html, "instance")
560
+ end
561
+ sample_gen = Generators::InstanceSampleGenerator.new(
562
+ component,
563
+ @parser,
564
+ @config
565
+ )
566
+ html << sample_gen.generate
567
+ end
568
+ end
569
+
570
+ # Generate schema component representation callout block
571
+ #
572
+ # @param html [Nokogiri::HTML::Builder] HTML builder
573
+ # @param component [Nokogiri::XML::Element] Schema component
574
+ def generate_schema_component_callout(html, component)
575
+ html.div(class: "bs-callout bs-callout-info") do
576
+ html.h4 do
577
+ html.text "Schema Component Representation"
578
+ html.text " "
579
+ generate_help_popover(html, "schema")
580
+ end
581
+ html.pre(class: "codehilite") do
582
+ html << format_xsd_component(component)
583
+ end
584
+ end
585
+ end
586
+
587
+ # Generate help popover button
588
+ #
589
+ # @param html [Nokogiri::HTML::Builder] HTML builder
590
+ # @param type [String] Type of help ("instance" or "schema")
591
+ def generate_help_popover(html, type)
592
+ content = if type == "instance"
593
+ instance_help_content
594
+ else
595
+ schema_help_content
596
+ end
597
+
598
+ html.span(class: "xs3p-panel-help") do
599
+ html.button(type: "button",
600
+ class: "btn btn-doc",
601
+ "data-container": "body",
602
+ "data-toggle": "popover",
603
+ "data-placement": type == "instance" ? "right" : "left",
604
+ "data-html": "true",
605
+ "data-content": content) do
606
+ html.span(class: "glyphicon glyphicon-question-sign") { html.text " " }
607
+ end
608
+ end
609
+ end
610
+
611
+ # Format XSD component for display
612
+ #
613
+ # @param component [Nokogiri::XML::Element] Schema component
614
+ # @return [String] Formatted XSD
615
+ def format_xsd_component(component)
616
+ # Get the component's XML representation
617
+ xml = component.to_xml(indent: 3)
618
+
619
+ # Add syntax highlighting classes
620
+ xml.gsub!(/<(\/?)([\w:]+)([^>]*)>/) do
621
+ tag_open = Regexp.last_match(1)
622
+ tag_name = Regexp.last_match(2)
623
+ attributes = Regexp.last_match(3)
624
+
625
+ # Highlight tag names
626
+ highlighted = "<span class=\"nt\">&lt;#{tag_open}"
627
+ highlighted += "<a href=\"#ns_#{tag_name.split(':').first}\" " \
628
+ "title=\"Find out namespace of '#{tag_name.split(':').first}' prefix\">" \
629
+ "#{tag_name}</a>" if tag_name.include?(":")
630
+ highlighted += tag_name unless tag_name.include?(":")
631
+
632
+ # Highlight attributes
633
+ if attributes && !attributes.empty?
634
+ attributes.gsub!(/(\w+)="([^"]*)"/) do
635
+ attr_name = Regexp.last_match(1)
636
+ attr_value = Regexp.last_match(2)
637
+ " <span class=\"na\">#{attr_name}=</span>" \
638
+ "<span class=\"s\">\"#{attr_value}\"</span>"
639
+ end
640
+ highlighted += attributes
641
+ end
642
+
643
+ highlighted += "&gt;</span>"
644
+ highlighted
645
+ end
646
+
647
+ xml
648
+ end
649
+
650
+ # Help content for instance representation
651
+ #
652
+ # @return [String] HTML help content
653
+ def instance_help_content
654
+ "The XML Instance Representation table shows the schema component's content as an XML instance. " \
655
+ "&lt;ul&gt;" \
656
+ "&lt;li&gt;The minimum and maximum occurrence of elements and attributes are provided in square brackets, e.g. [0..1].&lt;/li&gt;" \
657
+ "&lt;li&gt;Model group information are shown in gray, e.g. Start Choice ... End Choice.&lt;/li&gt;" \
658
+ "&lt;li&gt;For type derivations, the elements and attributes that have been added to or changed from the base type's content are shown in &lt;strong&gt;bold&lt;/strong&gt;&lt;/li&gt;" \
659
+ "&lt;li&gt;If an element/attribute has a fixed value, the fixed value is shown in green.&lt;/li&gt;" \
660
+ "&lt;/ul&gt;"
661
+ end
662
+
663
+ # Help content for schema component
664
+ #
665
+ # @return [String] HTML help content
666
+ def schema_help_content
667
+ "The Schema Component Representation table below displays the underlying XML representation of the schema component. (Annotations are not shown.)"
668
+ end
669
+
670
+ # Generate component ID for anchor links
671
+ #
672
+ # @param component_type [String] Component type
673
+ # @param component_name [String] Component name
674
+ # @return [String] Component ID
675
+ def generate_component_id(component_type, component_name)
676
+ case component_type
677
+ when "Element"
678
+ "element-#{component_name}"
679
+ when "Complex Type"
680
+ "type-#{component_name}"
681
+ when "Simple Type"
682
+ "type-#{component_name}"
683
+ when "Model Group"
684
+ "group-#{component_name}"
685
+ when "Attribute Group"
686
+ "attributeGroup-#{component_name}"
687
+ else
688
+ "#{component_type.downcase.tr(' ', '-')}-#{component_name}"
689
+ end
690
+ end
691
+
692
+ # Get the title for the documentation
693
+ #
694
+ # @return [String] Documentation title
695
+ def title
696
+ return @config.title if @config.title
697
+
698
+ # Try to extract meaningful title from schema
699
+ # 1. First global element name (often the root element)
700
+ if parser.elements.any?
701
+ first_element = parser.elements.first
702
+ return first_element["name"] if first_element["name"]
703
+ end
704
+
705
+ # 2. Extract from target namespace
706
+ if parser.target_namespace
707
+ namespace_parts = parser.target_namespace.split('/')
708
+ last_part = namespace_parts.last
709
+ # Extract meaningful name from namespace (e.g., "unitsml" from
710
+ # "https://schema.unitsml.org/unitsml/1.0")
711
+ if last_part && last_part != ""
712
+ # Remove version numbers
713
+ name = last_part.gsub(/[-_]?v?\d+(\.\d+)*$/, '')
714
+ return name unless name.empty?
715
+ end
716
+ end
717
+
718
+ # 3. Fallback
719
+ "XML Schema Documentation"
720
+ end
721
+ end
722
+ end
723
+ end