Class: Docbook::Mirror::MirrorToDocbook

Inherits:
Object
  • Object
show all
Defined in:
lib/docbook/mirror/mirror_to_docbook.rb

Overview

Transforms DocbookMirror node tree back into DocBook element tree.

This is the reverse direction of the transformation: ProseMirror-style JSON nodes are converted back into DocBook XML element objects, enabling round-trip fidelity (DocBook -> Mirror -> DocBook).

Usage: mirror_doc = DocbookToMirror.new.call(doc) docbook_el = MirrorToDocbook.new.call(mirror_doc)

Constant Summary collapse

TYPE_BUILDERS =
{
  "doc" => :docbook_document,
  "paragraph" => :docbook_paragraph,
  "text" => :docbook_text,
  "code_block" => :docbook_code_block,
  "blockquote" => :docbook_blockquote,
  "bullet_list" => :docbook_itemized_list,
  "ordered_list" => :docbook_ordered_list,
  "list_item" => :docbook_list_item,
  "dl" => :docbook_variable_list,
  "definition_term" => :docbook_definition_term,
  "definition_description" => :docbook_definition_description,
  "admonition" => :docbook_admonition,
  "chapter" => :docbook_chapter,
  "section" => :docbook_section,
  "part" => :docbook_part,
  "appendix" => :docbook_appendix,
  "reference" => :docbook_reference,
  "image" => :docbook_image,
}.freeze
ADMONITION_TYPES =
{
  "note" => Docbook::Elements::Note,
  "warning" => Docbook::Elements::Warning,
  "tip" => Docbook::Elements::Tip,
  "caution" => Docbook::Elements::Caution,
  "important" => Docbook::Elements::Important,
  "danger" => Docbook::Elements::Danger,
}.freeze
ROLE_CLASS_MAP =
{
  "userinput" => Docbook::Elements::UserInput,
  "computeroutput" => Docbook::Elements::ComputerOutput,
  "classname" => Docbook::Elements::ClassName,
}.freeze

Instance Method Summary collapse

Instance Method Details

#add_inline_to_para(para, element) ⇒ Object



135
136
137
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 135

def add_inline_to_para(para, element)
  para.try_add_inline(element)
end

#apply_mark_to_element(text, mark) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 139

def apply_mark_to_element(text, mark)
  case mark.type
  when "emphasis"
    el = Docbook::Elements::Emphasis.new
    el.content = [text]
    el
  when "strong"
    el = Docbook::Elements::Emphasis.new
    el.role = "bold"
    el.content = [text]
    el
  when "italic"
    el = Docbook::Elements::Emphasis.new
    el.role = "italic"
    el.content = [text]
    el
  when "code"
    role = (mark.attrs && (mark.attrs[:role] || mark.attrs["role"])) || "literal"
    el = role_to_class(role).new
    el.content = [text]
    el
  when "link"
    href = mark.attrs && (mark.attrs[:href] || mark.attrs["href"])
    el = Docbook::Elements::Link.new
    el.xlink_href = href
    el.content = [text]
    el
  when "xref"
    linkend = mark.attrs && (mark.attrs[:linkend] || mark.attrs["linkend"])
    el = Docbook::Elements::Xref.new
    el.linkend = linkend
    el.content = [text]
    el
  when "citation"
    bibref = mark.attrs && (mark.attrs[:bibref] || mark.attrs["bibref"])
    el = Docbook::Elements::Biblioref.new
    el.linkend = bibref
    el.content = [text]
    el
  when "tag"
    el = Docbook::Elements::Tag.new
    el.content = [text.gsub(/^<(.+)>$/, '\1')]
    el
  end
end

#apply_mark_to_text(text, mark) ⇒ Object



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 399

def apply_mark_to_text(text, mark)
  case mark.type
  when "emphasis"
    wrap_in_element(text, Docbook::Elements::Emphasis)
  when "strong"
    el = Docbook::Elements::Emphasis.new
    el.role = "bold"
    el.content = [text]
    el
  when "italic"
    el = Docbook::Elements::Emphasis.new
    el.role = "italic"
    el.content = [text]
    el
  when "code"
    role = (mark.attrs && (mark.attrs[:role] || mark.attrs["role"])) || "literal"
    wrap_in_element(text, role_to_class(role))
  when "link"
    href = mark.attrs && (mark.attrs[:href] || mark.attrs["href"])
    wrap_in_link(text, href)
  when "xref"
    linkend = mark.attrs && (mark.attrs[:linkend] || mark.attrs["linkend"])
    wrap_in_xref(text, linkend)
  when "citation"
    bibref = mark.attrs && (mark.attrs[:bibref] || mark.attrs["bibref"])
    wrap_in_biblioref(text, bibref)
  when "tag"
    tag = Docbook::Elements::Tag.new
    tag.content = [text.gsub(/^<(.+)>$/, '\1')]
    tag
  else
    text
  end
end

#build_section_like(klass, mirror_node) ⇒ Object



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 320

def build_section_like(klass, mirror_node)
  attrs = mirror_node.attrs || {}
  el = klass.new
  el.xml_id = attrs[:xml_id] || attrs["xml_id"]
  el.number = attrs[:number] || attrs["number"] if el.numberable?
  title_val = attrs[:title] || attrs["title"]
  if title_val
    title = Docbook::Elements::Title.new
    title.content = [title_val]
    el.title = title
  end
  if mirror_node.content
    el.para = mirror_node.content.to_a.compact.map { |n| to_docbook(n) }
  end
  el
end

#call(mirror_node) ⇒ Object

Entry point: Convert DocbookMirror node to DocBook element



47
48
49
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 47

def call(mirror_node)
  to_docbook(mirror_node)
end

#docbook_admonition(mirror_node) ⇒ Object



290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 290

def docbook_admonition(mirror_node)
  attrs = mirror_node.attrs || {}
  type = (attrs[:admonition_type] || attrs["admonition_type"] || "note").to_s

  adm_class = ADMONITION_TYPES[type] || Docbook::Elements::Note
  adm = adm_class.new
  if mirror_node.content
    adm.para = mirror_node.content.to_a.compact.map do |n|
      to_docbook(n)
    end
  end
  adm
end

#docbook_appendix(mirror_node) ⇒ Object



316
317
318
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 316

def docbook_appendix(mirror_node)
  build_section_like(Docbook::Elements::Appendix, mirror_node)
end

#docbook_blockquote(mirror_node) ⇒ Object



204
205
206
207
208
209
210
211
212
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 204

def docbook_blockquote(mirror_node)
  bq = Docbook::Elements::BlockQuote.new
  if mirror_node.content
    bq.para = mirror_node.content.to_a.compact.map do |n|
      to_docbook(n)
    end
  end
  bq
end

#docbook_chapter(mirror_node) ⇒ Object



304
305
306
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 304

def docbook_chapter(mirror_node)
  build_section_like(Docbook::Elements::Chapter, mirror_node)
end

#docbook_code_block(mirror_node) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 185

def docbook_code_block(mirror_node)
  attrs = mirror_node.attrs || {}
  language = attrs[:language] || attrs["language"]

  # Determine which code element to use
  el = if language == "text" || language.nil?
         Docbook::Elements::Screen.new
       else
         code = Docbook::Elements::Code.new
         code.language = language
         code
       end

  text = extract_text_from_content(mirror_node.content)
  el.content = [text]

  el
end

#docbook_definition_description(mirror_node) ⇒ Object



282
283
284
285
286
287
288
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 282

def docbook_definition_description(mirror_node)
  return nil unless mirror_node

  desc = Docbook::Elements::Para.new
  desc.content = process_docbook_inline(mirror_node)
  desc
end

#docbook_definition_term(mirror_node) ⇒ Object



274
275
276
277
278
279
280
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 274

def docbook_definition_term(mirror_node)
  return nil unless mirror_node

  term = Docbook::Elements::Term.new
  term.content = process_docbook_inline(mirror_node)
  term
end

#docbook_document(mirror_node) ⇒ Object

=========================================

Document



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 66

def docbook_document(mirror_node)
  attrs = mirror_node.attrs || {}
  title = attrs[:title] || attrs["title"]

  doc = Docbook::Elements::Article.new
  info = Docbook::Elements::Info.new
  if title
    title_el = Docbook::Elements::Title.new
    title_el.content = [title]
    info.title = title_el
  end
  doc.info = info

  if mirror_node.content
    doc.para = mirror_node.content.to_a.compact.map do |n|
      to_docbook(n)
    end
  end

  doc
end

#docbook_image(mirror_node) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 349

def docbook_image(mirror_node)
  attrs = mirror_node.attrs || {}
  figure = Docbook::Elements::Figure.new
  figure.xml_id = attrs[:xml_id] || attrs["xml_id"]
  if attrs[:title] || attrs["title"]
    title = Docbook::Elements::Title.new
    title.content = [attrs[:title] || attrs["title"]]
    figure.title = title
  end
  imageobject = Docbook::Elements::ImageObject.new
  imagedata = Docbook::Elements::ImageData.new
  imagedata.fileref = attrs[:src] || attrs["src"]
  imageobject.imagedata = imagedata
  figure.imageobject = [imageobject]
  figure
end

#docbook_itemized_list(mirror_node) ⇒ Object



214
215
216
217
218
219
220
221
222
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 214

def docbook_itemized_list(mirror_node)
  list = Docbook::Elements::ItemizedList.new
  if mirror_node.content
    list.listitem = mirror_node.content.to_a.compact.map do |n|
      docbook_list_item(n)
    end
  end
  list
end

#docbook_list_item(mirror_node) ⇒ Object



234
235
236
237
238
239
240
241
242
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 234

def docbook_list_item(mirror_node)
  li = Docbook::Elements::ListItem.new
  if mirror_node.content
    li.para = mirror_node.content.to_a.compact.map do |n|
      to_docbook(n)
    end
  end
  li
end

#docbook_ordered_list(mirror_node) ⇒ Object



224
225
226
227
228
229
230
231
232
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 224

def docbook_ordered_list(mirror_node)
  list = Docbook::Elements::OrderedList.new
  if mirror_node.content
    list.listitem = mirror_node.content.to_a.compact.map do |n|
      docbook_list_item(n)
    end
  end
  list
end

#docbook_paragraph(mirror_node) ⇒ Object

=========================================

Blocks



92
93
94
95
96
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 92

def docbook_paragraph(mirror_node)
  para = Docbook::Elements::Para.new
  process_paragraph_content(para, mirror_node)
  para
end

#docbook_part(mirror_node) ⇒ Object



312
313
314
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 312

def docbook_part(mirror_node)
  build_section_like(Docbook::Elements::Part, mirror_node)
end

#docbook_reference(mirror_node) ⇒ Object



337
338
339
340
341
342
343
344
345
346
347
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 337

def docbook_reference(mirror_node)
  attrs = mirror_node.attrs || {}
  ref = Docbook::Elements::RefEntry.new
  ref.xml_id = attrs[:xml_id] || attrs["xml_id"]
  if attrs[:title] || attrs["title"]
    refmeta = Docbook::Elements::RefMeta.new
    refmeta.refentrytitle = attrs[:title] || attrs["title"]
    ref.refmeta = refmeta
  end
  ref
end

#docbook_section(mirror_node) ⇒ Object



308
309
310
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 308

def docbook_section(mirror_node)
  build_section_like(Docbook::Elements::Section, mirror_node)
end

#docbook_text(mirror_node) ⇒ Object



387
388
389
390
391
392
393
394
395
396
397
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 387

def docbook_text(mirror_node)
  text = mirror_node.text || ""
  marks = mirror_node.marks || []

  # Process marks in order
  marks.each do |mark|
    text = apply_mark_to_text(text, mark)
  end

  text
end

#docbook_variable_list(mirror_node) ⇒ Object



244
245
246
247
248
249
250
251
252
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 244

def docbook_variable_list(mirror_node)
  vl = Docbook::Elements::VariableList.new
  if mirror_node.content
    vl.varlistentry = mirror_node.content.to_a.compact.map do |n|
      docbook_varlistentry(n)
    end
  end
  vl
end

#docbook_varlistentry(mirror_node) ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 254

def docbook_varlistentry(mirror_node)
  ve = Docbook::Elements::Varlistentry.new
  # Find term and description children
  if mirror_node.content
    term_node = mirror_node.content.to_a.find do |n|
      n.type == "definition_term"
    end
  end
  if mirror_node.content
    desc_node = mirror_node.content.to_a.find do |n|
      n.type == "definition_description"
    end
  end

  ve.term = docbook_definition_term(term_node) if term_node
  ve.definition = [docbook_definition_description(desc_node)] if desc_node

  ve
end

#extract_text_from_content(content) ⇒ Object

=========================================

Utilities



480
481
482
483
484
485
486
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 480

def extract_text_from_content(content)
  return "" unless content

  content.map do |node|
    node.text || extract_text_from_content(node.content)
  end.join
end

#process_docbook_inline(mirror_node) ⇒ Object

=========================================

Inline Content



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 370

def process_docbook_inline(mirror_node)
  inline_content = []

  return inline_content unless mirror_node.content

  mirror_node.content.each do |child|
    case child.type
    when "text"
      inline_content << docbook_text(child)
    when "soft_break"
      inline_content << "\n"
    end
  end

  inline_content
end

#process_paragraph_content(para, mirror_node) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 98

def process_paragraph_content(para, mirror_node)
  return unless mirror_node.content

  # Group content by type
  text_parts = []
  inline_elements = []

  mirror_node.content.each do |child|
    case child.type
    when "text"
      marks = child.marks || []
      if marks.empty?
        # Plain text - add to text parts
        text_parts << child.text
      else
        # Text with marks - create inline elements (don't add to text_parts)
        marks.each do |mark|
          inline_elements << { mark: mark, text: child.text }
        end
      end
    when "soft_break"
      text_parts << "\n"
    end
  end

  # Set content as joined text
  para.content = [text_parts.join]

  # Process inline elements into their collections
  inline_elements.each do |item|
    mark = item[:mark]
    text = item[:text]
    el = apply_mark_to_element(text, mark)
    add_inline_to_para(para, el) if el
  end
end

#role_to_class(role) ⇒ Object



446
447
448
449
450
451
452
453
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 446

def role_to_class(role)
  ROLE_CLASS_MAP[role.to_s] ||
    begin
      Docbook::Elements.const_get(role.to_s.split("_").map(&:capitalize).join)
    rescue StandardError
      Docbook::Elements::Literal
    end
end

#to_docbook(mirror_node) ⇒ Object

=========================================

Node dispatch

Raises:



55
56
57
58
59
60
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 55

def to_docbook(mirror_node)
  handler = TYPE_BUILDERS[mirror_node.type]
  raise Error, "Unknown node type: #{mirror_node.type}" unless handler

  send(handler, mirror_node)
end

#wrap_in_biblioref(text, bibref) ⇒ Object



469
470
471
472
473
474
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 469

def wrap_in_biblioref(text, bibref)
  biblioref = Docbook::Elements::Biblioref.new
  biblioref.linkend = bibref
  biblioref.content = [text]
  biblioref
end

#wrap_in_element(text, klass) ⇒ Object



434
435
436
437
438
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 434

def wrap_in_element(text, klass)
  el = klass.new
  el.content = [text]
  el
end


455
456
457
458
459
460
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 455

def wrap_in_link(text, href)
  link = Docbook::Elements::Link.new
  link.xlink_href = href
  link.content = [text]
  link
end

#wrap_in_xref(text, linkend) ⇒ Object



462
463
464
465
466
467
# File 'lib/docbook/mirror/mirror_to_docbook.rb', line 462

def wrap_in_xref(text, linkend)
  xref = Docbook::Elements::Xref.new
  xref.linkend = linkend
  xref.content = [text]
  xref
end