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.
- checksums.yaml +7 -0
- data/.github/workflows/rake.yml +16 -0
- data/.github/workflows/release.yml +25 -0
- data/.gitignore +72 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/.rubocop_todo.yml +432 -0
- data/CHANGELOG.adoc +446 -0
- data/Gemfile +21 -0
- data/LICENSE.adoc +29 -0
- data/README.adoc +386 -0
- data/Rakefile +11 -0
- data/examples/README.adoc +334 -0
- data/examples/advanced_usage.rb +286 -0
- data/examples/html_generation.rb +167 -0
- data/examples/parser_usage.rb +102 -0
- data/examples/schemas/person.xsd +171 -0
- data/examples/simple_generation.rb +149 -0
- data/exe/xseed +6 -0
- data/lib/xseed/cli.rb +376 -0
- data/lib/xseed/documentation/config.rb +101 -0
- data/lib/xseed/documentation/constants.rb +76 -0
- data/lib/xseed/documentation/generators/hierarchy_table_generator.rb +554 -0
- data/lib/xseed/documentation/generators/instance_sample_generator.rb +723 -0
- data/lib/xseed/documentation/generators/properties_table_generator.rb +983 -0
- data/lib/xseed/documentation/html_generator.rb +836 -0
- data/lib/xseed/documentation/html_generator.rb.bak +723 -0
- data/lib/xseed/documentation/presentation/css_generator.rb +510 -0
- data/lib/xseed/documentation/presentation/javascript_generator.rb +151 -0
- data/lib/xseed/documentation/presentation/navigation_builder.rb +169 -0
- data/lib/xseed/documentation/schema_loader.rb +121 -0
- data/lib/xseed/documentation/utils/helpers.rb +205 -0
- data/lib/xseed/documentation/utils/namespaces.rb +149 -0
- data/lib/xseed/documentation/utils/references.rb +135 -0
- data/lib/xseed/documentation/utils/strings.rb +75 -0
- data/lib/xseed/models/element_declaration.rb +144 -0
- data/lib/xseed/parser/xsd_parser.rb +192 -0
- data/lib/xseed/version.rb +5 -0
- data/lib/xseed.rb +76 -0
- data/xseed.gemspec +39 -0
- 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\"><#{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 += "></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
|
+
"<ul>" \
|
|
656
|
+
"<li>The minimum and maximum occurrence of elements and attributes are provided in square brackets, e.g. [0..1].</li>" \
|
|
657
|
+
"<li>Model group information are shown in gray, e.g. Start Choice ... End Choice.</li>" \
|
|
658
|
+
"<li>For type derivations, the elements and attributes that have been added to or changed from the base type's content are shown in <strong>bold</strong></li>" \
|
|
659
|
+
"<li>If an element/attribute has a fixed value, the fixed value is shown in green.</li>" \
|
|
660
|
+
"</ul>"
|
|
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
|