Class: Metanorma::Requirements::Modspec

Inherits:
Default
  • Object
show all
Defined in:
lib/metanorma/modspec/xrefs.rb,
lib/metanorma/modspec/isodoc.rb,
lib/metanorma/modspec/cleanup.rb,
lib/metanorma/modspec/modspec.rb,
lib/metanorma/modspec/validate.rb,
lib/metanorma/modspec/table_cleanup.rb,
lib/metanorma/modspec/reqt_label.rb

Defined Under Namespace

Classes: TSHash

Constant Summary collapse

REQT_TYPE_NORM =
{
  requirement: "general",
  recommendation: "general",
  permission: "general",
  requirements_class: "class",
  requirement_class: "class",
  recommendation_class: "class",
  permission_class: "class",
  conformance_test: "verification",
  conformance_class: "conformanceclass",
  abstract_test: "abstracttest",
}.freeze
CLASS2LABEL =
{
  general: "Requirement",
  verification: "Conformance test",
  class: "Requirement class",
  conformanceclass: "Conformance class",
  provision: "Provision",
  dependency: "Prerequisite",
  indirect_dependency: "Indirect prerequisite",
  implements: "Implemented provision",
}.freeze

Constants inherited from Default

Default::REQS

Instance Method Summary collapse

Methods inherited from Default

#attr_code, #csv_split, #descr_classif_extract, #descr_classif_render, #dl_to_attrs, #dl_to_elems, #l10n, #noko, #ns, #recommendation_attr_parse, #recommendation_labels, #req_classif_parse, #reqt_attrs, #reqt_dl_to_classif, #reqt_dl_to_classif1, #reqt_dl_to_classif2, #reqt_metadata_node?, #reqt_subpart?, #reqt_subpart_attrs, #requirement, #requirement_classification, #requirement_description_cleanup1, #requirement_description_wrap, #requirement_descriptions_cleanup, #requirement_elems, #requirement_inherit_cleanup, #requirement_inherit_insert, #requirement_inherit_insert1, #requirement_metadata1_attrs, #requirement_metadata1_set_insert, #requirement_metadata1_tags, #requirement_subpart, #wrap_in_para

Constructor Details

#initialize(options) ⇒ Modspec

Returns a new instance of Modspec.



8
9
10
11
# File 'lib/metanorma/modspec/modspec.rb', line 8

def initialize(options)
  super
  @modspecidentifierbase = @parent.modspecidentifierbase
end

Instance Method Details

#add_misc_container(xmldoc) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/metanorma/modspec/cleanup.rb', line 124

def add_misc_container(xmldoc)
  unless ins = xmldoc.at("//metanorma-extension")
    a = xmldoc.at("//termdocsource") || xmldoc.at("//bibdata")
    a.next = "<metanorma-extension/>"
    ins = xmldoc.at("//metanorma-extension")
  end
  ins
end

#add_misccontainer_anchor_aliases(xmldoc) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/metanorma/modspec/cleanup.rb', line 133

def add_misccontainer_anchor_aliases(xmldoc)
  m = add_misc_container(xmldoc)
  x = ".//table[@id = '_misccontainer_anchor_aliases']/tbody"
  unless ins = m.at(x)
    m << "<table id = '_misccontainer_anchor_aliases'><tbody/></table>"
    ins = m.at(x)
  end
  ins
end


15
16
17
18
19
20
21
# File 'lib/metanorma/modspec/table_cleanup.rb', line 15

def cell2link(table)
  table.xpath(ns(".//td")).each do |td|
    td.elements.empty? or next
    uri?(td.text.strip) or next
    td.children = "<link target='#{td.text.strip}'/>"
  end
end

#children_to_class(reqt, childclass, parentclass) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'lib/metanorma/modspec/validate.rb', line 100

def children_to_class(reqt, childclass, parentclass)
  return unless type2validate(reqt) == childclass

  r = @ids[:id][reqt["id"]]
  (r[:label] && @ids[:class][parentclass]&.any? do |x|
     x[:child].include?(r[:label])
   end) and return
  log_reqt(r, childclass, parentclass)
end

#class_to_children(reqt, type, childclass) ⇒ Object



92
93
94
95
96
97
98
# File 'lib/metanorma/modspec/validate.rb', line 92

def class_to_children(reqt, type, childclass)
  return unless type2validate(reqt) == type

  r = @ids[:id][reqt["id"]]
  !r[:child].empty? and return
  log_reqt(r, type, childclass)
end

#classif_tag(reqt, tag) ⇒ Object



137
138
139
140
141
142
143
# File 'lib/metanorma/modspec/validate.rb', line 137

def classif_tag(reqt, tag)
  reqt.xpath("./classification[tag][value]")
    .each_with_object([]) do |c, m|
    c.at("./tag").text.casecmp(tag).zero? or next
    m << c.at("./value").text
  end
end

#conflate_table_rows(trow) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/metanorma/modspec/table_cleanup.rb', line 44

def conflate_table_rows(trow)
  th = trow.at(ns("./th"))
  hdr = th.text
  th.children = @i18n.inflect(hdr, number: "pl")
  td = th.next_element
  res = [td.children.to_xml]
  res += gather_consec_table_rows(trow, hdr)
  td.children = res.join("<br/>")
end

#conflate_table_rows?(trow) ⇒ Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
# File 'lib/metanorma/modspec/table_cleanup.rb', line 37

def conflate_table_rows?(trow)
  tr1 = trow.next or return
  tr1.name == "tr" or return
  th = trow.at(ns("./th"))&.text
  th && th == tr1.at(ns("./th"))&.text
end

#conformance_to_reqt(reqt, reqtclass, confclass) ⇒ Object



82
83
84
85
86
87
88
89
90
# File 'lib/metanorma/modspec/validate.rb', line 82

def conformance_to_reqt(reqt, reqtclass, confclass)
  return unless type2validate(reqt) == confclass

  r = @ids[:id][reqt["id"]]
  (r[:subject] && @ids[:class][reqtclass]&.any? do |x|
     r[:subject].include?(x[:label])
   end) and return
  log_reqt(r, confclass, reqtclass)
end

#expand_xrefs_in_reqt(table) ⇒ Object

any xrefs not yet expanded out to rendering need to be expanded out, so that the identifier instances they contain can be truncated



98
99
100
101
102
103
# File 'lib/metanorma/modspec/table_cleanup.rb', line 98

def expand_xrefs_in_reqt(table)
  table.xpath(ns(".//xref[not(@style)][normalize-space(text()) = '']"))
    .each do |x|
    x << @xrefs.anchor(x["target"], :xref, false)
  end
end

#gather_consec_table_rows(trow, hdr) ⇒ Object



54
55
56
57
58
59
60
61
62
# File 'lib/metanorma/modspec/table_cleanup.rb', line 54

def gather_consec_table_rows(trow, hdr)
  ret = []
  trow.xpath("./following-sibling::xmlns:tr").each do |r|
    break unless r.at(ns("./th[text() = '#{hdr}']"))

    ret << r.remove.at(ns("./td")).children.to_xml
  end
  ret
end

#id_attr(node) ⇒ Object



138
139
140
# File 'lib/metanorma/modspec/isodoc.rb', line 138

def id_attr(node)
  node["id"] ? " id='#{node['id']}'" : ""
end

#init_lookups(doc) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/metanorma/modspec/reqt_label.rb', line 32

def init_lookups(doc)
  return if @init_lookups

  @init_lookups = true
  @reqtlabels = reqtlabels(doc)
  @reqt_ids = reqt_ids(doc)
  @reqt_links_class = reqt_links_class(doc)
  @reqt_links_test = reqt_links_test(doc)
  @reqt_id_base = reqt_id_base(doc)
  truncate_id_base_outside_reqts(doc)
end

#inject_crossreference_reqt?(node, label) ⇒ Boolean

embedded reqts xref to reqts via label lookup

Returns:

  • (Boolean)


51
52
53
54
# File 'lib/metanorma/modspec/reqt_label.rb', line 51

def inject_crossreference_reqt?(node, label)
  !node.ancestors("requirement, recommendation, permission").empty? and
    @reqtlabels[label]
end

#log_cycle(link, path) ⇒ Object



175
176
177
178
179
180
# File 'lib/metanorma/modspec/validate.rb', line 175

def log_cycle(link, path)
  @log.add("Requirements", nil, <<~MSG
    Cycle in Modspec linkages through #{link}: #{(path << path.first).join(' => ')}
  MSG
  )
end

#log_reqt(reqt, leftclass, rightclass) ⇒ Object



49
50
51
52
53
54
# File 'lib/metanorma/modspec/validate.rb', line 49

def log_reqt(reqt, leftclass, rightclass)
  @log.add("Requirements", reqt[:elem], <<~MSG
    #{CLASS2LABEL[leftclass.to_sym]} #{reqt[:label] || reqt[:id]} has no corresponding #{CLASS2LABEL[rightclass.to_sym]}
  MSG
  )
end

#log_reqt2(reqt, leftclass, target, rightclass) ⇒ Object



56
57
58
59
60
61
# File 'lib/metanorma/modspec/validate.rb', line 56

def log_reqt2(reqt, leftclass, target, rightclass)
  @log.add("Requirements", reqt[:elem], <<~MSG
    #{CLASS2LABEL[leftclass]} #{reqt[:label] || reqt[:id]} points to #{CLASS2LABEL[rightclass]} #{target} outside this document
  MSG
  )
end

#nested_reqt?(reqt) ⇒ Boolean

Returns:

  • (Boolean)


13
14
15
16
# File 'lib/metanorma/modspec/validate.rb', line 13

def nested_reqt?(reqt)
  reqt.at("./ancestor::requirement | ./ancestor::recommendation | " \
          "./ancestor::permission")
end

#permission_parts(block, block_id, label, klass) ⇒ Object



62
63
64
65
66
67
68
69
70
# File 'lib/metanorma/modspec/xrefs.rb', line 62

def permission_parts(block, block_id, label, klass)
  block.xpath(ns("./component[@class = 'part']"))
    .each_with_index.with_object([]) do |(c, i), m|
    next if c["id"].nil? || c["id"].empty?

    m << { id: c["id"], number: l10n("#{block_id} #{(i + 'A'.ord).chr}"),
           elem: c, label: label, klass: klass }
  end
end

#postprocess_anchor_struct(block, anchor) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/metanorma/modspec/xrefs.rb', line 72

def postprocess_anchor_struct(block, anchor)
  super
  anchor[:xref_bare] = anchor[:xref]
  l = block.at(ns("./identifier")) and
    anchor[:xref] += l10n(": ") +
      "<tt><xref style='id' target='#{block['id']}'>#{l.text}</xref></tt>"
  anchor[:modspec] = anchor[:xref]
  anchor
end

#preserve_in_nested_table?(node) ⇒ Boolean

Returns:

  • (Boolean)


180
181
182
183
# File 'lib/metanorma/modspec/isodoc.rb', line 180

def preserve_in_nested_table?(node)
  %w(recommendation requirement permission
     table ol dl ul).include?(node.name)
end

#rec_subj(node) ⇒ Object



192
193
194
195
196
197
# File 'lib/metanorma/modspec/reqt_label.rb', line 192

def rec_subj(node)
  case node["type"]
  when "class" then @labels["modspec"]["targettype"]
  else @labels["default"]["subject"]
  end
end

#rec_target(node) ⇒ Object



199
200
201
202
203
204
205
206
# File 'lib/metanorma/modspec/reqt_label.rb', line 199

def rec_target(node)
  case node["type"]
  when "class" then @labels["modspec"]["targettype"]
  when "conformanceclass" then @labels["modspec"]["requirementclass"]
  when "verification", "abstracttest" then @labels["default"]["requirement"]
  else @labels["modspec"]["target"]
  end
end

#recommend_class(node) ⇒ Object



208
209
210
211
212
213
214
# File 'lib/metanorma/modspec/reqt_label.rb', line 208

def recommend_class(node)
  case node["type"]
  when "verification", "abstracttest" then "recommendtest"
  when "class", "conformanceclass" then "recommendclass"
  else "recommend"
  end
end

#recommend_component_label(node) ⇒ Object



223
224
225
226
227
228
229
230
231
# File 'lib/metanorma/modspec/reqt_label.rb', line 223

def recommend_component_label(node)
  c = case node["class"]
      when "test-purpose" then "Test purpose"
      when "test-method" then "Test method"
      else node["class"]
      end
  @labels["default"][c] || @labels["modspec"][c] ||
    Metanorma::Utils.strict_capitalize_first(c)
end

#recommend_name_class(node) ⇒ Object



216
217
218
219
220
221
# File 'lib/metanorma/modspec/reqt_label.rb', line 216

def recommend_name_class(node)
  if %w(verification abstracttest).include?(node["type"])
    "RecommendationTestTitle"
  else "RecommendationTitle"
  end
end

#recommend_title(node, out) ⇒ Object



70
71
72
73
74
75
76
77
# File 'lib/metanorma/modspec/isodoc.rb', line 70

def recommend_title(node, out)
  label = node.at(ns("./identifier")) or return
  ret = <<~OUTPUT
    <tr><th>#{@labels['modspec']['identifier']}</th>
    <td><tt><modspec-ident>#{label.children.to_xml}</modspec-ident></tt></td>
  OUTPUT
  out.add_child(ret)
end

#recommendation_attr_keyvalue(node, key, value) ⇒ Object



165
166
167
168
169
170
171
172
# File 'lib/metanorma/modspec/isodoc.rb', line 165

def recommendation_attr_keyvalue(node, key, value)
  tag = node.at(ns("./#{key}")) or return nil
  value = node.at(ns("./#{value}")) or return nil
  !%w(target indirect-dependency identifier-base
      implements).include?(tag.text.downcase) or
    return nil
  [Metanorma::Utils.strict_capitalize_first(tag.text), value.children]
end

#recommendation_attributes(node, out) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/metanorma/modspec/isodoc.rb', line 61

def recommendation_attributes(node, out)
  ins = out.add_child("<tbody></tbody>").first
  recommend_title(node, ins)
  recommendation_attributes1(node).each do |i|
    ins.add_child("<tr><th>#{i[0]}</th><td>#{i[1]}</td></tr>")
  end
  ins
end

#recommendation_attributes1(node) ⇒ Object



79
80
81
82
83
84
85
86
# File 'lib/metanorma/modspec/isodoc.rb', line 79

def recommendation_attributes1(node)
  ret = recommendation_attributes1_head(node, [])
  node.xpath(ns("./classification")).each do |c|
    line = recommendation_attr_keyvalue(c, "tag",
                                        "value") and ret << line
  end
  ret
end

#recommendation_attributes1_component(node, out) ⇒ Object



156
157
158
159
160
161
162
163
# File 'lib/metanorma/modspec/isodoc.rb', line 156

def recommendation_attributes1_component(node, out)
  return out if node["class"] == "guidance"

  node = recommendation_steps(node)
  out << "<tr#{id_attr(node)}><th>#{node['label']}</th>" \
         "<td>#{node.children}</td></tr>"
  out
end

#recommendation_attributes1_dependencies(node, head) ⇒ Object



114
115
116
117
# File 'lib/metanorma/modspec/isodoc.rb', line 114

def recommendation_attributes1_dependencies(node, head)
  head = recommendation_attributes1_inherit(node, head)
  recommendation_attributes_dependencies2(node, head)
end

#recommendation_attributes1_head(node, head) ⇒ Object



88
89
90
91
92
93
94
95
96
# File 'lib/metanorma/modspec/isodoc.rb', line 88

def recommendation_attributes1_head(node, head)
  oblig = node["obligation"] and
    head << [@labels["default"]["obligation"], oblig]
  subj = node.at(ns("./subject"))&.children and
    head << [rec_subj(node), subj]
  head = recommendation_attributes1_target(node, head)
  head += recommendation_backlinks(node)
  recommendation_attributes1_dependencies(node, head)
end

#recommendation_attributes1_inherit(node, head) ⇒ Object



119
120
121
122
123
124
125
# File 'lib/metanorma/modspec/isodoc.rb', line 119

def recommendation_attributes1_inherit(node, head)
  node.xpath(ns("./inherit")).each do |i|
    head << [@labels["modspec"]["dependency"],
             recommendation_id(i.children.to_xml)]
  end
  head
end

#recommendation_attributes1_target(node, head) ⇒ Object



98
99
100
101
102
103
104
105
# File 'lib/metanorma/modspec/isodoc.rb', line 98

def recommendation_attributes1_target(node, head)
  node.xpath(ns("./classification[tag][value]")).each do |c|
    c.at(ns("./tag")).text.casecmp("target").zero? or next
    xref = recommendation_id(c.at(ns("./value")).text) and
      head << [rec_target(node), xref]
  end
  head
end

#recommendation_attributes_dependencies2(node, head) ⇒ Object



127
128
129
130
131
132
133
134
135
136
# File 'lib/metanorma/modspec/isodoc.rb', line 127

def recommendation_attributes_dependencies2(node, head)
  %w(indirect-dependency implements).each do |x|
    node.xpath(ns("./classification[tag][value]")).each do |c|
      c.at(ns("./tag")).text.casecmp(x).zero? or next
      xref = recommendation_id(c.at(ns("./value")).children.to_xml) and
        head << [@labels["modspec"][x.gsub(/-/, "")], xref]
    end
  end
  head
end


107
108
109
110
111
112
# File 'lib/metanorma/modspec/isodoc.rb', line 107

def recommendation_backlinks(node)
  ret = []
  id = node.at(ns("./identifier")) or return ret
  ret = recommendation_backlinks_test(node, id, ret)
  recommendation_backlinks_class(node, id, ret)
end


174
175
176
177
178
179
180
# File 'lib/metanorma/modspec/reqt_label.rb', line 174

def recommendation_backlinks_class(node, id, ret)
  (node["type"].nil? || node["type"].empty? ||
  node["type"] == "verification") and
    xref = recommendation_link_class(id.text) and
    ret << [@labels["modspec"]["included_in"], xref]
  ret
end


166
167
168
169
170
171
172
# File 'lib/metanorma/modspec/reqt_label.rb', line 166

def recommendation_backlinks_test(node, id, ret)
  (%w(general class).include?(node["type"]) &&
    xref = recommendation_link_test(id.text)) or return ret
  lbl = node["type"] == "general" ? "conformancetest" : "conformanceclass"
  ret << [@labels["modspec"][lbl], xref]
  ret
end

#recommendation_base(node, _klass) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/metanorma/modspec/isodoc.rb', line 14

def recommendation_base(node, _klass)
  out = node.document.create_element("table")
  out.default_namespace = node.namespace.href
  %w(id keep-with-next keep-lines-together unnumbered).each do |x|
    out[x] = node[x] if node[x]
  end
  out["class"] = "modspec"
  out["type"] = recommend_class(node)
  recommendation_component_labels(node)
  out
end

#recommendation_class_label(node) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/metanorma/modspec/reqt_label.rb', line 56

def recommendation_class_label(node)
  case node["type"]
  when "verification" then @labels["modspec"]["conformancetest"]
  when "class" then @labels["modspec"]["#{node.name}class"]
  when "abstracttest" then @labels["modspec"]["abstracttest"]
  when "conformanceclass" then @labels["modspec"]["conformanceclass"]
  else
    case node.name
    when "recommendation" then @labels["default"]["recommendation"]
    when "requirement" then @labels["default"]["requirement"]
    when "permission" then @labels["default"]["permission"]
    end
  end
end

#recommendation_component_labels(node) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/metanorma/modspec/isodoc.rb', line 26

def recommendation_component_labels(node)
  node.xpath(ns("./component[@class = 'part']")).each_with_index do |c, i|
    c["label"] = (i + "A".ord).chr.to_s
  end
  node.xpath(ns("./component[not(@class = 'part')]")).each do |c|
    c["label"] = recommend_component_label(c)
  end
end

#recommendation_header(reqt, out) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/metanorma/modspec/isodoc.rb', line 35

def recommendation_header(reqt, out)
  n = recommendation_name(reqt, nil)
  x = if reqt.ancestors("requirement, recommendation, permission").empty?
        "<thead><tr><th scope='colgroup' colspan='2'>" \
          "<p class='#{recommend_name_class(reqt)}'>#{n}</p>" \
          "</th></tr></thead>"
      else
        "<thead><tr><th>#{recommendation_class_label(reqt)}</th>" \
          "<td>#{n}</td></tr></thead>"
      end
  out << x
  out
end

#recommendation_id(ident) ⇒ Object



161
162
163
164
# File 'lib/metanorma/modspec/reqt_label.rb', line 161

def recommendation_id(ident)
  test = @reqt_ids[ident&.strip] or return ident&.strip
  "<xref target='#{test[:id]}'>#{test[:lbl]}</xref>"
end

#recommendation_label(elem, type, xrefs) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
# File 'lib/metanorma/modspec/reqt_label.rb', line 11

def recommendation_label(elem, type, xrefs)
  @xrefs ||= xrefs.dup
  init_lookups(elem.document)
  label = elem.at(ns("./identifier"))&.text
  if inject_crossreference_reqt?(elem, label)
    recommendation_label_xref(elem, label, xrefs, type)
  else
    type = recommendation_class_label(elem)
    super
  end
end

#recommendation_label_xref(elem, label, xrefs, type) ⇒ Object



23
24
25
26
27
28
29
30
# File 'lib/metanorma/modspec/reqt_label.rb', line 23

def recommendation_label_xref(elem, label, xrefs, type)
  id = @reqtlabels[label]
  number = xrefs.anchor(id, :modspec, false)
  number.nil? and return type
  elem.ancestors("requirement, recommendation, permission").empty? and
    return number
  "<xref target='#{id}'>#{number}</xref>"
end


156
157
158
159
# File 'lib/metanorma/modspec/reqt_label.rb', line 156

def recommendation_link_class(ident)
  test = @reqt_links_class[ident&.strip] or return nil
  "<xref target='#{test[:id]}'>#{test[:lbl]}</xref>"
end


104
105
106
107
# File 'lib/metanorma/modspec/reqt_label.rb', line 104

def recommendation_link_test(ident)
  test = @reqt_links_test[ident&.strip] or return nil
  "<xref target='#{test[:id]}'>#{test[:lbl]}</xref>"
end

#recommendation_name(node, _out) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/metanorma/modspec/isodoc.rb', line 49

def recommendation_name(node, _out)
  ret = ""
  name = node.at(ns("./name")) and ret += name.children.to_xml
  title = node.at(ns("./title"))
  return ret unless title &&
    node.ancestors("requirement, recommendation, permission").empty?

  ret += ": " unless !name || name.text.empty?
  ret += title.children.to_xml
  l10n(ret)
end

#recommendation_steps(node) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/metanorma/modspec/isodoc.rb', line 142

def recommendation_steps(node)
  node.elements.each { |e| recommendation_steps(e) }
  return node unless node.at(ns("./component[@class = 'step']"))

  d = node.at(ns("./component[@class = 'step']"))
  d = d.replace("<ol class='steps'><li#{id_attr(d)}>" \
                "#{d.children.to_xml}</li></ol>").first
  node.xpath(ns("./component[@class = 'step']")).each do |f|
    f = f.replace("<li#{id_attr(f)}>#{f.children.to_xml}</li>").first
    d << f
  end
  node
end

#req_class_pathsObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/metanorma/modspec/xrefs.rb', line 4

def req_class_paths
  [
    { klass: "permissionclass",
      label: @labels["modspec"]["permissionclass"],
      xpath: "permission[@type = 'class']" },
    { klass: "requirementclass",
      label: @labels["modspec"]["requirementclass"],
      xpath: "requirement[@type = 'class']" },
    { klass: "recommendationclass",
      label: @labels["modspec"]["recommendationclass"],
      xpath: "recommendation[@type = 'class']" },
    { klass: "permissiontest",
      label: @labels["modspec"]["conformancetest"],
      xpath: "permission[@type = 'verification']" },
    { klass: "recommendationtest",
      label: @labels["modspec"]["conformancetest"],
      xpath: "recommendation[@type = 'verification']" },
    { klass: "requirementtest",
      label: @labels["modspec"]["conformancetest"],
      xpath: "requirement[@type = 'verification']" },
    { klass: "abstracttest",
      label: @labels["modspec"]["abstracttest"],
      xpath: "permission[@type = 'abstracttest']" },
    { klass: "abstracttest",
      label: @labels["modspec"]["abstracttest"],
      xpath: "requirement[@type = 'abstracttest']" },
    { klass: "abstracttest",
      label: @labels["modspec"]["abstracttest"],
      xpath: "recommendation[@type = 'abstracttest']" },
    { klass: "conformanceclass",
      label: @labels["modspec"]["conformanceclass"],
      xpath: "permission[@type = 'conformanceclass']" },
    { klass: "conformanceclass",
      label: @labels["modspec"]["conformanceclass"],
      xpath: "requirement[@type = 'conformanceclass']" },
    { klass: "conformanceclass",
      label: @labels["modspec"]["conformanceclass"],
      xpath: "recommendation[@type = 'conformanceclass']" },
    { klass: "permission",
      label: @labels["default"]["permission"],
      xpath: "permission[not(@type = 'verification' or @type = 'class' " \
             "or @type = 'abstracttest' or @type = 'conformanceclass')]" },
    { klass: "recommendation",
      label: @labels["default"]["recommendation"],
      xpath: "recommendation[not(@type = 'verification' or " \
             "@type = 'class' or @type = 'abstracttest' or " \
             "@type = 'conformanceclass')]" },
    { klass: "requirement",
      label: @labels["default"]["requirement"],
      xpath: "requirement[not(@type = 'verification' or @type = 'class' " \
             "or @type = 'abstracttest' or @type = 'conformanceclass')]" },
  ]
end

#req_nested_class_pathsObject



58
59
60
# File 'lib/metanorma/modspec/xrefs.rb', line 58

def req_nested_class_paths
  req_class_paths
end

#reqt_component_type(node) ⇒ Object



174
175
176
177
178
# File 'lib/metanorma/modspec/isodoc.rb', line 174

def reqt_component_type(node)
  klass = node.name
  klass == "component" and klass = node["class"]
  "requirement-#{klass}"
end

#reqt_cycles_validateObject



155
156
157
158
159
160
161
# File 'lib/metanorma/modspec/validate.rb', line 155

def reqt_cycles_validate
  @cycles_validated and return
  @cycles_validated = true
  %i(child dependency indirect_dependency implements).each do |x|
    reqt_cycles_validate1(x)
  end
end

#reqt_cycles_validate1(link) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/metanorma/modspec/validate.rb', line 163

def reqt_cycles_validate1(link)
  arr = TSHash.new(@ids[:id].values)
  arr.link = link
  TSort.each_strongly_connected_component(
    lambda { |&b| arr.tsort_each_node(&b) },
    lambda { |n, &b| arr.tsort_each_child(n, &b) },
  ) do |c|
    c.size == 1 and next
    log_cycle(link, c)
  end
end

#reqt_cycles_validate_dependencyObject



201
# File 'lib/metanorma/modspec/validate.rb', line 201

def reqt_cycles_validate_dependency; end

#reqt_dl(node, out) ⇒ Object



218
219
220
221
222
223
224
225
226
# File 'lib/metanorma/modspec/isodoc.rb', line 218

def reqt_dl(node, out)
  node.xpath(ns("./dt")).each do |dt|
    dd = dt.next_element
    dd&.name == "dd" or next
    out.add_child("<tr><th>#{dt.children.to_xml}</th>" \
                  "<td>#{dd.children.to_xml}</td></tr>")
  end
  out
end

#reqt_extract_id_base(reqt) ⇒ Object



98
99
100
101
102
# File 'lib/metanorma/modspec/reqt_label.rb', line 98

def reqt_extract_id_base(reqt)
  reqt.xpath(ns("./classification[tag][value]")).detect do |x|
    x.at(ns("./tag")).text.casecmp("identifier-base").zero?
  end&.at(ns("./value"))
end

#reqt_extract_target(reqt) ⇒ Object



92
93
94
95
96
# File 'lib/metanorma/modspec/reqt_label.rb', line 92

def reqt_extract_target(reqt)
  reqt.xpath(ns("./classification[tag][value]")).detect do |x|
    x.at(ns("./tag")).text.casecmp("target").zero?
  end&.at(ns("./value"))
end

#reqt_hierarchy_extractObject



129
130
131
132
133
134
# File 'lib/metanorma/modspec/reqt_label.rb', line 129

def reqt_hierarchy_extract
  @reqt_links_class.each_with_object({}) do |(k, v), m|
    m[v[:id]] ||= []
    m[v[:id]] << @reqt_ids[k][:id]
  end
end

#reqt_id_base(docxml) ⇒ Object



149
150
151
152
153
154
# File 'lib/metanorma/modspec/reqt_label.rb', line 149

def reqt_id_base(docxml)
  ret = reqt_id_base_init(docxml)
  ret = reqt_id_base_inherit(ret, reqt_hierarchy_extract)
  @modspecidentifierbase or return ret
  ret.each_key { |k| ret[k] ||= @modspecidentifierbase }
end

#reqt_id_base_inherit(ret, class2reqt) ⇒ Object



140
141
142
143
144
145
146
147
# File 'lib/metanorma/modspec/reqt_label.rb', line 140

def reqt_id_base_inherit(ret, class2reqt)
  ret.each_key do |k|
    class2reqt[k]&.each do |k1|
      ret[k1] ||= ret[k]
    end
  end
  ret
end

#reqt_id_base_init(docxml) ⇒ Object



136
137
138
# File 'lib/metanorma/modspec/reqt_label.rb', line 136

def reqt_id_base_init(docxml)
  docxml.reqt_iter { |r, m| m[r["id"]] = reqt_extract_id_base(r)&.text }
end

#reqt_ids(docxml) ⇒ Object



71
72
73
74
75
76
77
# File 'lib/metanorma/modspec/reqt_label.rb', line 71

def reqt_ids(docxml)
  docxml.reqt_iter do |r, m|
    id = r.at(ns("./identifier")) or next
    m[id.text] =
      { id: r["id"], lbl: @xrefs.anchor(r["id"], :modspec, false) }
  end
end


18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/metanorma/modspec/validate.rb', line 18

def reqt_link_validate(reqt)
  return if nested_reqt?(reqt)

  reqt_to_conformance(reqt, "general", "verification")
  reqt_to_conformance(reqt, "class", "conformanceclass")
  conformance_to_reqt(reqt, "general", "verification")
  conformance_to_reqt(reqt, "class", "conformanceclass")
  class_to_children(reqt, "class", "general")
  class_to_children(reqt, "conformanceclass", "verification")
  children_to_class(reqt, "verification", "conformanceclass")
  reqt_to_dependency(reqt)
end


110
111
112
113
114
115
116
117
# File 'lib/metanorma/modspec/validate.rb', line 110

def reqt_links(docxml)
  docxml.xpath("//requirement | //recommendation | //permission")
    .each_with_object({ id: {}, class: {}, label: {} }) do |r, m|
      next if nested_reqt?(r)

      reqt_links1(r, m, type2validate(r), reqt_links_struct(r))
    end
end

#reqt_links1(reqt, hash, type, struct) ⇒ Object



119
120
121
122
123
124
# File 'lib/metanorma/modspec/validate.rb', line 119

def reqt_links1(reqt, hash, type, struct)
  hash[:id][reqt["id"]] = struct
  hash[:class][type] ||= []
  hash[:class][type] << struct
  reqt_links1_label(reqt, hash, struct)
end

#reqt_links1_label(reqt, hash, struct) ⇒ Object



126
127
128
129
130
131
132
133
134
135
# File 'lib/metanorma/modspec/validate.rb', line 126

def reqt_links1_label(reqt, hash, struct)
  return hash unless struct[:label]

  if hash[:label][struct[:label]]
    msg = "Modspec identifier #{struct[:label]} is used more than once"
    @log.add("Requirements", reqt, msg, severity: 0)
  end
  hash[:label][struct[:label]] = reqt["id"]
  hash
end

we have not implemented multiple levels of nesting of classes



110
111
112
113
114
115
116
117
118
119
# File 'lib/metanorma/modspec/reqt_label.rb', line 110

def reqt_links_class(docxml)
  docxml.reqt_iter do |r, m|
    %w(class conformanceclass).include?(r["type"]) or next
    id = r.at(ns("./identifier")) or next
    r.xpath(ns("./requirement | ./recommendation | ./permission"))
      .each do |r1|
      m = reqt_links_class1(id, r, r1, m)
    end
  end
end


121
122
123
124
125
126
127
# File 'lib/metanorma/modspec/reqt_label.rb', line 121

def reqt_links_class1(id, parent_reqt, reqt, acc)
  id1 = reqt.at(ns("./identifier")) or return acc
  lbl = @xrefs.anchor(@reqt_ids[id.text.strip][:id], :modspec, false)
  lbl or return acc
  acc[id1.text] = { lbl: lbl, id: parent_reqt["id"] }
  acc
end


145
146
147
148
149
150
151
152
153
# File 'lib/metanorma/modspec/validate.rb', line 145

def reqt_links_struct(reqt)
  { id: reqt["id"], elem: reqt, label: reqt.at("./identifier")&.text,
    subject: classif_tag(reqt, "target"),
    child: reqt.xpath("./requirement | ./recommendation | ./permission")
      .map { |r| r.at("./identifier")&.text },
    dependency: reqt.xpath("./inherit").map(&:text),
    indirect_dependency: classif_tag(reqt, "indirect-dependency"),
    implements: classif_tag(reqt, "implements") }
end


79
80
81
# File 'lib/metanorma/modspec/reqt_label.rb', line 79

def reqt_links_test(docxml)
  docxml.reqt_iter { |r, m| reqt_links_test1(r, m) }
end


83
84
85
86
87
88
89
90
# File 'lib/metanorma/modspec/reqt_label.rb', line 83

def reqt_links_test1(reqt, acc)
  %w(conformanceclass verification).include?(reqt["type"]) or return
  subj = reqt_extract_target(reqt)
  id = reqt.at(ns("./identifier")) or return
  lbl = @xrefs.anchor(@reqt_ids[id.text.strip][:id], :modspec, false)
  (subj && lbl) or return
  acc[subj.text] = { lbl: lbl, id: reqt["id"] }
end

#reqt_to_conformance(reqt, reqtclass, confclass) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/metanorma/modspec/validate.rb', line 72

def reqt_to_conformance(reqt, reqtclass, confclass)
  return unless type2validate(reqt) == reqtclass

  r = @ids[:id][reqt["id"]]
  (r[:label] && @ids[:class][confclass]&.any? do |x|
     x[:subject].include?(r[:label])
   end) and return
  log_reqt(r, reqtclass, confclass)
end

#reqt_to_dependency(reqt) ⇒ Object



63
64
65
66
67
68
69
70
# File 'lib/metanorma/modspec/validate.rb', line 63

def reqt_to_dependency(reqt)
  r = @ids[:id][reqt["id"]]
  %i(dependency indirect_dependency implements).each do |x|
    r[x].each do |d|
      @ids[:label][d] or log_reqt2(r, :provision, d, x)
    end
  end
end

#reqtlabels(doc) ⇒ Object



44
45
46
47
48
# File 'lib/metanorma/modspec/reqt_label.rb', line 44

def reqtlabels(doc)
  doc.reqt_iter do |r, m|
    l = r.at(ns("./identifier"))&.text and m[l] = r["id"]
  end
end

#requirement_anchor_aliases(reqt) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/metanorma/modspec/cleanup.rb', line 143

def requirement_anchor_aliases(reqt)
  x = reqt.xpath("./identifier")
  x.empty? and return
  table = add_misccontainer_anchor_aliases(reqt.document)
  ids = x.each_with_object([]) do |i, m|
    m << "<td>#{i.text}</td>"
  end
  table << "<tr><th>#{reqt['id']}</th>#{ids.join}</tr>"
end

#requirement_component_parse(node, out) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/metanorma/modspec/isodoc.rb', line 185

def requirement_component_parse(node, out)
  node["exclude"] == "true" and return out
  descr_classif_render(node)
  node.elements.size == 1 && node.first_element_child.name == "dl" and
    return reqt_dl(node.first_element_child, out)
  node.name == "component" and
    return recommendation_attributes1_component(node, out)
  node.name == "description" and
    return requirement_description_parse(node, out)
  out.add_child("<tr#{id_attr(node)}><td colspan='2'></td></tr>").first
    .at(ns(".//td")) <<
    (preserve_in_nested_table?(node) ? node : node.children)
  out
end

#requirement_description_parse(node, out) ⇒ Object



200
201
202
203
204
205
206
207
# File 'lib/metanorma/modspec/isodoc.rb', line 200

def requirement_description_parse(node, out)
  lbl = "description"
  recommend_class(node.parent) == "recommend" and
    lbl = "statement"
  out << "<tr><th>#{@labels['modspec'][lbl]}</th>" \
         "<td>#{node.children.to_xml}</td></tr>"
  out
end

#requirement_guidance_parse(node, out) ⇒ Object



209
210
211
212
213
214
215
216
# File 'lib/metanorma/modspec/isodoc.rb', line 209

def requirement_guidance_parse(node, out)
  ins = out.at(ns("./tbody"))
  node.xpath(ns("./component[@class = 'guidance']")).each do |f|
    ins << "<tr#{id_attr(f)}><th>#{@labels['modspec']['guidance']}</th>" \
           "<td>#{f.children}</td></tr>"
  end
  out
end

#requirement_identifier_cleanup(reqt) ⇒ Object



153
154
155
156
# File 'lib/metanorma/modspec/cleanup.rb', line 153

def requirement_identifier_cleanup(reqt)
  super
  requirement_anchor_aliases(reqt)
end

#requirement_metadata1(reqt, dlist, ins) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/metanorma/modspec/cleanup.rb', line 34

def requirement_metadata1(reqt, dlist, ins)
  ins1 = super
  dlist.xpath("./dt").each do |e|
    tag = e.text&.gsub(/ /, "-")&.downcase
    tag = "description" if tag == "statement"
    next unless .include?(tag)

    ins1.next = requirement_metadata1_component(e, tag)
    ins1 = ins1.next
  end
end

#requirement_metadata1_component(term, tag) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/metanorma/modspec/cleanup.rb', line 46

def requirement_metadata1_component(term, tag)
  val = term.at("./following::dd")
  val.name = tag
  val.xpath("./dl").each do |d|
    requirement_metadata1(val, d, d)
    d.remove
  end
  .include?(term.text) or
    return val
  requirement_metadata1_component_val2id(val)
end

#requirement_metadata1_component_val2id(val) ⇒ Object



58
59
60
61
62
63
64
65
# File 'lib/metanorma/modspec/cleanup.rb', line 58

def requirement_metadata1_component_val2id(val)
  if val.at(".//link") && val.text.strip.empty?
    val.children = "<identifier>#{val.at('.//link')['target']}</identifier>"
  elsif !val.text.strip.empty?
    val.children = "<identifier>#{val.text.strip}</identifier>"
  end
  val
end

#requirement_metadata_cleanup(reqt) ⇒ Object

separate from default model requirement_metadata_cleanup,

which extracts model

ogc into reqt



69
70
71
72
73
74
75
# File 'lib/metanorma/modspec/cleanup.rb', line 69

def (reqt)
  super
  (reqt)
  (reqt)
  requirement_subparts_to_blocks(reqt)
  requirement_target_identifiers(reqt)
end

#requirement_metadata_component_tagsObject



22
23
24
25
26
# File 'lib/metanorma/modspec/cleanup.rb', line 22

def 
  %w(test-purpose test-method test-method-type conditions part description
     statement reference step guidance) +
    
end

#requirement_metadata_requirement_tagsObject



28
29
30
31
32
# File 'lib/metanorma/modspec/cleanup.rb', line 28

def 
  %w(conformance-test conformance-class abstract-test requirement-class
     recommendation-class permission-class requirement permission
     recommendation)
end

#requirement_metadata_to_component(reqt) ⇒ Object



86
87
88
89
90
91
92
93
# File 'lib/metanorma/modspec/cleanup.rb', line 86

def (reqt)
  xpath =  - %w(statement description) -
    
  reqt.xpath(xpath.map { |x| ".//#{x}" }.join(" | ")).each do |c|
    c["class"] = c.name
    c.name = "component"
  end
end

#requirement_metadata_to_requirement(reqt) ⇒ Object



95
96
97
98
99
100
101
102
# File 'lib/metanorma/modspec/cleanup.rb', line 95

def (reqt)
  xpath = 
  reqt.xpath(xpath.map { |x| "./#{x}" }.join(" | ")).each do |c|
    c["id"] = Metanorma::Utils::anchor_or_uuid
    c["model"] = reqt["model"] # all requirements must have a model
    (c)
  end
end

#requirement_metadata_to_requirement1(reqt) ⇒ Object



104
105
106
107
108
109
110
111
112
113
# File 'lib/metanorma/modspec/cleanup.rb', line 104

def (reqt)
  reqt["type"] = reqt.name.sub(/-/, "_")
  reqt.name =
    case reqt.name
    when "recommendation-class", "recommendation" then "recommendation"
    when "permission-class", "permission" then "permission"
    else "requirement"
    end
  requirement_type_cleanup(reqt)
end

#requirement_render1(node) ⇒ Object



8
9
10
11
12
# File 'lib/metanorma/modspec/isodoc.rb', line 8

def requirement_render1(node)
  init_lookups(node.document)
  ret = requirement_guidance_parse(node, super)
  requirement_table_cleanup(node, ret)
end

#requirement_subparts_to_blocks(reqt) ⇒ Object



115
116
117
118
119
120
121
122
# File 'lib/metanorma/modspec/cleanup.rb', line 115

def requirement_subparts_to_blocks(reqt)
  reqt.xpath(".//component | .//description").each do |c|
    next if %w(p ol ul dl table component description)
      .include?(c&.elements&.first&.name)

    c.children = "<p>#{c.children.to_xml}</p>"
  end
end

#requirement_table_cleanup(node, table) ⇒ Object



6
7
8
9
10
11
12
13
# File 'lib/metanorma/modspec/table_cleanup.rb', line 6

def requirement_table_cleanup(node, table)
  table = requirement_table_nested_cleanup(node, table)
  requirement_table_consec_rows_cleanup(node, table)
  node.ancestors("requirement, recommendation, permission").empty? and
    truncate_id_base_in_reqt(table)
  cell2link(table)
  table
end

#requirement_table_consec_rows_cleanup(_node, table) ⇒ Object



30
31
32
33
34
35
# File 'lib/metanorma/modspec/table_cleanup.rb', line 30

def requirement_table_consec_rows_cleanup(_node, table)
  table.xpath(ns("./tbody/tr")).each do |tr|
    conflate_table_rows?(tr) or next
    conflate_table_rows(tr)
  end
end

#requirement_table_nested_cleanup(node, table) ⇒ Object



64
65
66
67
68
69
70
71
72
# File 'lib/metanorma/modspec/table_cleanup.rb', line 64

def requirement_table_nested_cleanup(node, table)
  table.xpath(ns("./tbody/tr/td/table")).each do |t|
    x = t.at(ns("./thead/tr")) or next
    x.at(ns("./th")).children =
      requirement_table_nested_cleanup_hdr(node)
    t.parent.parent.replace(x)
  end
  table
end

#requirement_table_nested_cleanup_hdr(node) ⇒ Object



74
75
76
77
78
# File 'lib/metanorma/modspec/table_cleanup.rb', line 74

def requirement_table_nested_cleanup_hdr(node)
  label = "provision"
  node["type"] == "conformanceclass" and label = "conformancetest"
  @i18n.get["requirements"]["modspec"][label]
end

#requirement_target_identifiers(reqt) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/metanorma/modspec/cleanup.rb', line 77

def requirement_target_identifiers(reqt)
  reqt.xpath("./classification[tag][value/link]").each do |c|
    %w(target indirect-dependency implements identifier-base)
      .include?(c.at("./tag").text.downcase) or next
    v = c.at("./value[link]")
    v.children = v.at("./link/@target").text
  end
end

#requirement_type_cleanup(reqt) ⇒ Object



17
18
19
20
# File 'lib/metanorma/modspec/cleanup.rb', line 17

def requirement_type_cleanup(reqt)
  ret = REQT_TYPE_NORM[reqt["type"]&.to_sym] or return
  reqt["type"] = ret
end

#strip_id_base(elem, base) ⇒ Object



80
81
82
83
84
# File 'lib/metanorma/modspec/table_cleanup.rb', line 80

def strip_id_base(elem, base)
  return elem.children if base.nil?

  elem.children.to_xml.delete_prefix(base)
end

#truncate_id_base_in_reqt(table) ⇒ Object



105
106
107
108
109
# File 'lib/metanorma/modspec/table_cleanup.rb', line 105

def truncate_id_base_in_reqt(table)
  base = @reqt_id_base[table["id"]]
  expand_xrefs_in_reqt(table)
  truncate_id_base_in_reqt1(table, base)
end

#truncate_id_base_in_reqt1(table, base) ⇒ Object



86
87
88
89
90
91
92
93
94
# File 'lib/metanorma/modspec/table_cleanup.rb', line 86

def truncate_id_base_in_reqt1(table, base)
  table.xpath(ns(".//xref[@style = 'id']")).each do |x|
    @reqt_id_base[x["target"]] or next # is a modspec requirement
    x.children = strip_id_base(x, base)
  end
  table.xpath(ns(".//modspec-ident")).each do |x|
    x.replace(strip_id_base(x, base))
  end
end

#truncate_id_base_outside_reqts(docxml) ⇒ Object



182
183
184
185
186
187
188
189
190
# File 'lib/metanorma/modspec/reqt_label.rb', line 182

def truncate_id_base_outside_reqts(docxml)
  @modspecidentifierbase or return
  (docxml.xpath(ns("//xref[@style = 'id']")) - docxml
    .xpath(ns("//requirement//xref | //permission//xref | " \
              "//recommendation//xref"))).each do |x|
    @reqt_id_base[x["target"]] or next # is a modspec requirement
    x.children = x.children.to_xml.delete_prefix(@modspecidentifierbase)
  end
end

#type2validate(reqt) ⇒ Object



31
32
33
34
35
36
# File 'lib/metanorma/modspec/validate.rb', line 31

def type2validate(reqt)
  type = reqt["type"]
  type = "general" if type.nil? || type.empty?
  type = "verification" if type == "abstracttest"
  type
end

#uri?(string) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
26
27
28
# File 'lib/metanorma/modspec/table_cleanup.rb', line 23

def uri?(string)
  uri = URI.parse(string)
  %w(http https).include?(uri.scheme)
rescue URI::BadURIError, URI::InvalidURIError
  false
end

#validate(reqt, log) ⇒ Object



6
7
8
9
10
11
# File 'lib/metanorma/modspec/validate.rb', line 6

def validate(reqt, log)
  @log ||= log
  @ids ||= reqt_links(reqt.document)
  reqt_cycles_validate
  reqt_link_validate(reqt)
end