junoser 0.3.12 → 0.4.2

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.
data/lib/junoser/ruler.rb CHANGED
@@ -14,10 +14,9 @@ module Junoser
14
14
  str = @rule.read
15
15
  str = remove_comments(str)
16
16
  str = process_reserved_element(str)
17
- str = str.split(/\n/).map {|l| format(process_line(l)) }.join("\n")
17
+ str = str.split(/\n/).map { |l| format(process_line(l)) }.join("\n")
18
18
  end
19
19
 
20
-
21
20
  private
22
21
 
23
22
  def remove_comments(str)
@@ -27,16 +26,16 @@ module Junoser
27
26
  def process_line(str)
28
27
  return str if str =~ /^(.* do|end)$/
29
28
 
30
- str.gsub!(/("[^"]+")/) { "str(#$1)" } # "foo" -> str("foo")
29
+ str.gsub!(/("[^"]+")/) { "str(#$1)" } # "foo" -> str("foo")
31
30
 
32
- str.gsub!(/^(\s*)arg(\.as\(:\S+\))? \($/) { "#{$1}b(arg#$2," } # arg ( -> b(arg,
33
- str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f\(|,]+)(\.as\(:\S+\))?(,?)$/) { "#{$1}a(#$2, #$3)#$4#$5" } # str("foo") bar -> a(str("foo"), bar)
34
- str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\)(,?)$/) { "#{$1}a(#$2, #$3#$4)#$5" } # str("foo") (a | b) -> a(str("foo"), a | b)
31
+ str.gsub!(/^(\s*)arg(\.as\(:\S+\))? \($/) { "#{$1}b(arg#$2," } # arg ( -> b(arg,
32
+ str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f\(|,]+)(\.as\(:\S+\))?(,?)$/) { "#{$1}a(#$2, #$3)#$4#$5" } # str("foo") bar -> a(str("foo"), bar)
33
+ str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\)(,?)$/) { "#{$1}a(#$2, #$3#$4)#$5" } # str("foo") (a | b) -> a(str("foo"), a | b)
35
34
 
36
- str.gsub!(/^(\s*)(str\(\S+\)) \($/) { "#{$1}b(#$2," } # str("foo") ( -> b(str("foo"),
37
- str.gsub!(/^(\s*)(enum)?(\(.*\))(\.as\(:\S\))? \($/) { "#{$1}b(#$2#$3#$4," } # (a | b) ( -> b((a | b),
38
- str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f\(|,]+) \($/) { "#{$1}b(a(#$2, #$3)," } # str("foo") bar ( -> b(a(str("foo"), bar),
39
- str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\) \($/) { "#{$1}a(#$2, #$3#$4," } # str("foo") (a | b) ( -> a(str("foo"), a | b,
35
+ str.gsub!(/^(\s*)(str\(\S+\)) \($/) { "#{$1}b(#$2," } # str("foo") ( -> b(str("foo"),
36
+ str.gsub!(/^(\s*)(enum)?(\(.*\))(\.as\(:\S\))? \($/) { "#{$1}b(#$2#$3#$4," } # (a | b) ( -> b((a | b),
37
+ str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f\(|,]+) \($/) { "#{$1}b(a(#$2, #$3)," } # str("foo") bar ( -> b(a(str("foo"), bar),
38
+ str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\) \($/) { "#{$1}a(#$2, #$3#$4," } # str("foo") (a | b) ( -> a(str("foo"), a | b,
40
39
 
41
40
  str
42
41
  end
@@ -46,64 +45,106 @@ module Junoser
46
45
 
47
46
  str.gsub! /"groups" \(\s*s\(\s*any\s*\)\s*\)/, 'a("groups", arg, configuration)'
48
47
 
49
- %w[as-number confederation-as metric-value limit-threshold filename filter-name class-name classifier-name link-subscription per-traffic-class-bandwidth template-name].each do |key|
50
- str.gsub! %["#{key}" arg], 'arg'
51
- end
52
-
53
48
  str.gsub! '"equal-literal"', '"="'
54
49
  str.gsub! '"plus-literal"', '"+"'
55
50
  str.gsub! '"minus-literal"', '"-"'
56
51
 
57
- str.gsub!(/\((.*) \| "name"\)/) { "(#$1 | arg)" }
58
- str.gsub! '"vlan" ("all" | "vlan-name")', '"vlan" ("all" | arg)'
59
- str.gsub!(/\((.*) \| "vlan-id"\)/) { "(#$1 | arg)" }
52
+ #
53
+ # Statements can be quoted
54
+ #
60
55
  str.gsub!(/("ssh-\S+") arg/) { "#$1 (quote | arg)" }
56
+ str.gsub! '"message" arg', '"message" (quote | arg)'
61
57
  str.gsub! '"description" arg', '"description" (quote | arg)'
62
58
  str.gsub! '"as-path-prepend" arg', '"as-path-prepend" (quote | arg)'
63
- str.gsub!(/arg \| (".*")/) { "#$1 | arg" }
64
- str.gsub! '"dhcp-service" (', '("dhcp-service" | "dhcp") ('
65
59
 
66
- str.gsub!(/"inet"(.*)"inet6"/) { %["inet6"#$1"inet"] }
67
- str.gsub!(/"icmp"(.*)"icmp6"/) { %["icmp6"#$1"icmp"] }
68
- str.gsub!(/"icmp"(.*)"icmpv6"/) { %["icmpv6"#$1"icmp"] }
69
- str.gsub!(/"http"(.*)"https"/) { %["https"#$1"http"] }
70
- str.gsub!(/"snmp"(.*)"snmptrap"/) { %["snmptrap"#$1"snmp"] }
71
- %w[ccc ethernet-over-atm tcc vpls bridge].each do |encap|
72
- str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %["ethernet-#{encap}"#$1"ethernet"] }
60
+ str.gsub!(/^(\s*)"as-path" arg \(\s*c\(\s*arg/) do
61
+ format(['"as-path" arg (',
62
+ ' c(',
63
+ ' quote | arg'], $1)
73
64
  end
74
- str.gsub! '"icmp6" |', '"icmp6" | "icmpv6" |'
75
- str.gsub!(/"cspf"(.*)"cspf-link"/) { %["cspf-link"#$1"cspf"] }
76
- str.gsub!(/"route-filter" (\(\s*control_route_filter_type\s*\))/) { %["route-filter" arg #{$1}.as(:oneline)] }
77
- str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) { %["source-adress-filter" arg #{$1}.as(:oneline)] }
78
65
 
79
- %w[teardown hold-time stub].each do |key|
80
- str.gsub!(/^(\s*"#{key}" \(\s*)c\(/) { "#{$1}sc(" }
66
+ str.gsub!(/^rule\(:regular_expression\) do\s*((?!end).)*\s*end/) do
67
+ <<~EOS
68
+ rule(:regular_expression) do
69
+ (quote | arg).as(:arg)
70
+ end
71
+ EOS
81
72
  end
82
- %w[file confederation].each do |key|
83
- str.gsub!(/^(\s*"#{key}" \(\s*)c\(\s*arg,/) { "#{$1}sca(" }
73
+
74
+ str.gsub!(/^rule\(:login_user_object\) do\s*arg\.as\(:arg\) \(\s*c\(\s*"full-name" arg,/) do
75
+ <<~EOS
76
+ rule(:login_user_object) do
77
+ arg.as(:arg) (
78
+ sc(
79
+ "full-name" (quote | arg),
80
+ EOS
84
81
  end
85
- %w[exact longer orlonger].each do |key|
86
- str.gsub!(/^(\s*"#{key}") arg/) { "#{$1}" }
82
+
83
+ str.gsub!(/^(\s*)"location" arg,\s*"contact" arg,/) do
84
+ format(['"location" (quote | arg),',
85
+ '"contact" (quote | arg),'], $1)
87
86
  end
88
87
 
88
+ str.gsub!(/^(\s*)"as-path" \(\s*c\(\s*"path" arg/) do
89
+ format(['"as-path" (',
90
+ ' c(',
91
+ ' "path" (quote | arg)'], $1)
92
+ end
89
93
 
90
- str.gsub!(/^(\s*)"ieee-802.3ad" \(\s*c\(\s*"lacp" \(\s*c\(/) do
91
- format(['"802.3ad" (',
92
- ' ca(',
93
- ' "lacp" (',
94
- ' c(',
95
- ' "force-up",'], $1)
94
+ str.gsub!(/^(\s*)prefix_list_items,\s*"apply-path" arg/) do
95
+ format(['"apply-path" (quote | arg),',
96
+ 'prefix_list_items'], $1)
96
97
  end
97
- str.gsub!(/^(\s*)"as-path" arg \(\s*c\(\s*arg/) do
98
- format(['"as-path" arg (',
99
- ' c(',
100
- ' quote | arg'], $1)
98
+
99
+ #
100
+ # "arg" matches anything so move to the end
101
+ #
102
+ str.gsub!(/arg \| (".*")/) { "#$1 | arg" }
103
+ str.gsub!(/^(\s*)c\(\s*arg,$/) { "#{$1}ca(" }
104
+ str.gsub!(/(rule\(:control_route_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
105
+ str.gsub!(/(rule\(:control_source_address_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
106
+ str.gsub!(/^(rule\(:trace_file_type\) do\s*)ca\(/) { "#{$1}sca(" }
107
+
108
+ str.gsub!(/^(rule\(:archive_object\) do\s*)c\(/) { "#{$1}sc(" }
109
+ str.gsub!(/^(rule\(:server_group_type\) do\s*)c\(\s*c\(\s*arg\s*\)\s*\)/) { "#{$1}s(arg, arg)" }
110
+
111
+ str.gsub!(/^(rule\(:rib_group_inet_type\) do)\s*c\(\s*arg/) do
112
+ format([$1,
113
+ ' ca(',
114
+ ' a(arg, arg)'], '')
101
115
  end
102
- str.gsub!(/^(\s*)"priority" \(\s*c\(\s*arg,\s*arg\s*\)/) do
116
+
117
+ # Fix overkill
118
+ str.gsub!(/^(\s*)"priority" \(\s*ca\(\s*arg\s*\)/) do
103
119
  format(['"priority" (',
104
120
  ' a(arg, arg)', $1])
105
121
  end
106
- str.gsub!(/^(\s*)"path" arg \(\s*c\(\s*c\(\s*"abstract",\s*c\(\s*"loose",\s*"loose-link",\s*"strict"\s*\)\s*\)\.as\(:oneline\)/) do
122
+
123
+ #
124
+ # Longer pattern first
125
+ #
126
+ # covers:
127
+ # "inet" | "inet6"
128
+ #
129
+ # and even
130
+ #
131
+ # "inet",
132
+ # "inet6"
133
+ str.gsub!(/"cspf"(.*\s*.*)"cspf-link"/) { %["cspf-link"#$1"cspf"] }
134
+ str.gsub!(/"http"(.*\s*.*)"https"/) { %["https"#$1"http"] }
135
+ str.gsub!(/"inet"(.*\s*.*)"inet6"/) { %["inet6"#$1"inet"] }
136
+ str.gsub!(/"icmp"(.*\s*.*)"icmp6"/) { %["icmp6"#$1"icmp"] }
137
+ str.gsub!(/"icmp"(.*\s*.*)"icmpv6"/) { %["icmpv6"#$1"icmp"] }
138
+ str.gsub!(/"snmp"(.*\s*.*)"snmptrap"/) { %["snmptrap"#$1"snmp"] }
139
+ str.gsub!(/"ospf"(.*\s*.*)"ospf3"/) { %["ospf3"#$1"ospf"] }
140
+ str.gsub! '"tls1" | "tls11" | "tls12"', '"tls11" | "tls12" | "tls1"'
141
+ str.gsub!(/("group1" \| "group2" \| "group5") \| ([^\)]+)/) { "#{$2} | #{$1}"}
142
+
143
+ %w[ccc ethernet-over-atm tcc vpls bridge].each do |encap|
144
+ str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %["ethernet-#{encap}"#$1"ethernet"] }
145
+ end
146
+
147
+ str.gsub!(/^(\s*)"path" arg \(\s*c\(\s*sc\(\s*"abstract",\s*c\(\s*"loose",\s*"loose-link",\s*"strict"\s*\)\s*\)\.as\(:oneline\)/) do
107
148
  format(['"path" arg (',
108
149
  ' c(',
109
150
  ' b(',
@@ -119,63 +160,75 @@ module Junoser
119
160
  ' )', $1])
120
161
  end
121
162
 
122
- str.gsub!(/^(\s*)c\(\s*\("default"\)\s*\)/) do
123
- format(['c(',
124
- ' ("default" | arg)',
125
- ')'], $1)
126
- end
163
+ #
164
+ # Fix .xsd: Elements without "nokeyword" flag
165
+ #
166
+ str.gsub!(/\((.*) \| "name"\)/) { "(#$1 | arg)" }
167
+ str.gsub! '"vlan" ("all" | "vlan-name")', '"vlan" ("all" | arg)'
168
+ str.gsub!(/\((.*) \| "vlan-id"\)/) { "(#$1 | arg)" }
127
169
 
128
- str.gsub!(/^rule\(:regular_expression\) do\s*((?!end).)*\s*end/) do
129
- format(['rule(:regular_expression) do',
130
- ' (quote | arg).as(:arg)',
131
- 'end'])
132
- end
133
- str.gsub!(/^rule\(:login_user_object\) do\s*arg\.as\(:arg\) \(\s*c\(\s*"full-name" arg,/) do
134
- format(['rule(:login_user_object) do',
135
- ' arg.as(:arg) (',
136
- ' sc(',
137
- ' "full-name" (quote | arg),'])
170
+ %w[filename].each do |key|
171
+ str.gsub! %["#{key}" arg], 'arg'
138
172
  end
139
173
 
140
- str.gsub!(/(rule\(:control_route_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
141
- str.gsub!(/(rule\(:control_source_address_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
142
- str.gsub!(/^(rule\(:trace_file_type\) do\s*)c\(\s*arg,/) { "#{$1}sca(" }
143
- str.gsub!(/^(rule\(:archive_object\) do\s*)c\(/) { "#{$1}sc(" }
144
- str.gsub!(/^(rule\(:server_group_type\) do\s*)c\(\s*c\(\s*arg\s*\)\s*\)/) { "#{$1}s(arg, arg)" }
145
- str.gsub!(/^(rule\(:rib_group_inet_type\) do)\s*c\(\s*arg/) do
146
- format([$1,
147
- ' ca(',
148
- ' a(arg, arg)'], '')
149
- end
174
+ # "filename" fix above leaves "arg". Move to the end
175
+ str.gsub!(/^(rule\(:esp_trace_file_type\) do\s*)c\(\s*arg,/) { "#{$1}ca(" }
150
176
 
151
- str.gsub!(/^(\s*)c\(\s*arg,$/) { "#{$1}ca(" }
177
+ # Fix .xsd: system processes dhcp is valid on some platforms
178
+ str.gsub! '"dhcp-service" (', '("dhcp-service" | "dhcp") ('
152
179
 
153
- str.gsub!(/^(\s*)"location" arg,\s*"contact" arg,/) do
154
- format(['"location" (quote | arg),',
155
- '"contact" (quote | arg),'], $1)
180
+ # Fix .xsd: "icmpv6" is also acceptable
181
+ str.gsub! '"icmp6" |', '"icmp6" | "icmpv6" |'
182
+
183
+ #
184
+ # Fix .xsd: "arg" is missing
185
+ #
186
+ str.gsub!(/"route-filter" (\(\s*control_route_filter_type\s*\))/) { %["route-filter" arg #{$1}.as(:oneline)] }
187
+ str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) { %["source-adress-filter" arg #{$1}.as(:oneline)] }
188
+ %w[file].each do |key|
189
+ str.gsub!(/^(\s*"#{key}" \(\s*)c\(\s*arg,/) { "#{$1}sca(" }
156
190
  end
157
191
 
158
- str.gsub!(/^(\s*)"as-path" \(\s*c\(\s*"path" arg/) do
159
- format(['"as-path" (',
160
- ' c(',
161
- ' "path" (quote | arg)'], $1)
192
+ # Fix .xsd: Unnecessary "arg" is added
193
+ %w[exact longer orlonger].each do |key|
194
+ str.gsub!(/^(\s*"#{key}") arg/) { "#{$1}" }
162
195
  end
163
196
 
197
+ # Fix .xsd: "ieee-802.3ad" is invalid
198
+ str.gsub! '"ieee-802.3ad"', '"802.3ad"'
199
+
200
+ # Fix .xsd: "class-of-service interfaces all unit * classifiers exp foo"
201
+ str.gsub!(/^(\s*)sc\(\s*\("default"\)\s*\)/) do
202
+ format(['c(',
203
+ ' ("default" | arg)',
204
+ ')'], $1)
205
+ end
206
+
207
+ # Fix .xsd: "from-zone" arg is also required
164
208
  str.gsub!(/^(\s*)"policy" \(\s*s\(\s*arg,\s*"to-zone-name" arg,\s*c\(\s*"policy" \(\s*policy_type\s*\)\s*\)/) do
165
209
  format(['b(s("from-zone", arg, "to-zone", arg),',
166
210
  ' b("policy", policy_type',
167
211
  ], $1)
168
212
  end
169
213
 
214
+ # Fix .xsd: "members" accepts [ foo bar ]
215
+ str.gsub! '"members" arg', '"members" any'
216
+
217
+ # Fix .xsd: "term_object" accepts multiple conditions
218
+ str.gsub!(/^(rule\(:term_object\) do\s*arg\.as\(:arg\) \(\s*)c\(/) { "#{$1}sc(" }
219
+
220
+ # Fix .xsd: keywords "dest-nat-rule-match", "src-nat-rule-match", "static-nat-rule-match" are wrong
221
+ str.gsub!(/"(dest|src|static)-nat-rule-match"/) { '"match"' }
222
+
170
223
  str
171
224
  end
172
225
 
173
- def format(str, offset=OFFSET)
226
+ def format(str, offset = OFFSET)
174
227
  case str
175
228
  when String
176
229
  str.empty? ? '' : offset + str
177
230
  when Array
178
- str.map {|s| s.empty? ? '' : offset + s.to_s }.join("\n")
231
+ str.map { |s| s.empty? ? '' : offset + s.to_s }.join("\n")
179
232
  end
180
233
  end
181
234
 
@@ -206,7 +259,7 @@ module Junoser
206
259
 
207
260
  begin
208
261
  # .xsd doesn't include "apply-groups"
209
- if line =~ /(.*)\\s+apply-groups\\s+(\\S+|\\[.*\\])$/
262
+ if line =~ /(.*)\\s+apply-groups(-except)?\\s+(\\S+|\\[.*\\])$/
210
263
  return \$1 == 'set' ? true : parse(\$1)
211
264
  end
212
265
 
@@ -1,3 +1,3 @@
1
1
  module Junoser
2
- VERSION = "0.3.12"
2
+ VERSION = "0.4.2"
3
3
  end
@@ -1,13 +1,14 @@
1
1
  module Junoser
2
2
  module Xsd
3
3
  module Base
4
- attr_reader :xml
4
+ attr_reader :xml, :parent
5
5
 
6
6
  OFFSET = ' '
7
7
 
8
8
  def initialize(xml, options={})
9
9
  @xml = xml
10
10
  @depth = options[:depth] || 0
11
+ @parent = options[:parent]
11
12
  end
12
13
 
13
14
  def config
@@ -32,15 +33,17 @@ module Junoser
32
33
  ']>'].join("\n#{OFFSET*(@depth+1)}")
33
34
  end
34
35
 
35
-
36
- private
37
-
38
36
  def oneliner?
39
- @oneliner ||= !xml.xpath('./xsd:annotation/xsd:appinfo/flag[text()="oneliner"]').empty?
37
+ # don't use "/flag" as xsd manipulation automatically add "xsd:" prefix
38
+ @oneliner ||= !xml.xpath('./xsd:annotation/xsd:appinfo/*[local-name()="flag" and text()="oneliner"]').empty?
40
39
  end
41
40
 
41
+
42
+ private
43
+
42
44
  def nokeyword?
43
- @nokeyword ||= !xml.xpath('./xsd:annotation/xsd:appinfo/flag[text()="nokeyword"]').empty?
45
+ # don't use "/flag" as xsd manipulation automatically add "xsd:" prefix
46
+ @nokeyword ||= !xml.xpath('./xsd:annotation/xsd:appinfo/*[local-name()="flag" and text()="nokeyword"]').empty?
44
47
  end
45
48
 
46
49
  def has_single_child_of?(klass)
@@ -10,9 +10,9 @@ module Junoser
10
10
  @config ||= children.map {|child|
11
11
  case child.name
12
12
  when 'element'
13
- Junoser::Xsd::Element.new(child, depth: @depth+1)
13
+ Junoser::Xsd::Element.new(child, depth: @depth+1, parent: self)
14
14
  when 'choice'
15
- Junoser::Xsd::Choice.new(child, depth: @depth+1)
15
+ Junoser::Xsd::Choice.new(child, depth: @depth+1, parent: self)
16
16
  else
17
17
  raise "ERROR: unknown element: #{child.name}"
18
18
  end
@@ -29,6 +29,10 @@ module Junoser
29
29
  format('c(', config.map(&:to_s).join(",\n"), ')')
30
30
  end
31
31
  end
32
+
33
+ def unbounded?
34
+ xml['maxOccurs'] == 'unbounded'
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -18,9 +18,9 @@ module Junoser
18
18
  @config ||= children.map {|child|
19
19
  case child.name
20
20
  when 'sequence'
21
- Junoser::Xsd::Sequence.new(child, depth: @depth+1)
21
+ Junoser::Xsd::Sequence.new(child, depth: @depth+1, parent: self)
22
22
  when 'simpleContent'
23
- Junoser::Xsd::SimpleContent.new(child, depth: @depth+1)
23
+ Junoser::Xsd::SimpleContent.new(child, depth: @depth+1, parent: self)
24
24
  when 'attribute'
25
25
  'arg'
26
26
  else
@@ -52,7 +52,7 @@ module Junoser
52
52
  def argument
53
53
  return unless @argument
54
54
 
55
- arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1).config
55
+ arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1, parent: self).config
56
56
  raise "ERROR: argument shouldn't consist of multiple elements" if arg.size > 1
57
57
 
58
58
  if root?
@@ -16,9 +16,9 @@ module Junoser
16
16
  @config ||= children.map {|child|
17
17
  case child.name
18
18
  when 'complexType'
19
- Junoser::Xsd::ComplexType.new(child, depth: @depth+1)
19
+ Junoser::Xsd::ComplexType.new(child, depth: @depth+1, parent: self)
20
20
  when 'simpleType'
21
- Junoser::Xsd::SimpleType.new(child, depth: @depth+1)
21
+ Junoser::Xsd::SimpleType.new(child, depth: @depth+1, parent: self)
22
22
  else
23
23
  raise "ERROR: unknown element: #{child.name}"
24
24
  end
@@ -70,7 +70,7 @@ module Junoser
70
70
  when @argument.name == 'simpleType'
71
71
  'arg'
72
72
  else
73
- arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1).config
73
+ arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1, parent: self).config
74
74
  raise "ERROR: argument shouldn't consist of multiple elements" if arg.size > 1
75
75
  arg.first.to_s.strip
76
76
  end
@@ -97,6 +97,10 @@ module Junoser
97
97
 
98
98
  def documentation
99
99
  @documentation ||= xml.xpath('./xsd:annotation/xsd:documentation').text
100
+
101
+ # Translate multiline documentation into a single line to make it parsable in further processes
102
+ @documentation.gsub! /\n\s*/, ' '
103
+
100
104
  @documentation.empty? ? nil : @documentation
101
105
  end
102
106
  end
@@ -17,9 +17,9 @@ module Junoser
17
17
  @config ||= children.map {|child|
18
18
  case child.name
19
19
  when 'enumeration'
20
- Junoser::Xsd::Enumeration.new(child, depth: @depth+1)
20
+ Junoser::Xsd::Enumeration.new(child, depth: @depth+1, parent: self)
21
21
  when 'simpleType'
22
- Junoser::Xsd::SimpleType.new(child, depth: @depth+1)
22
+ Junoser::Xsd::SimpleType.new(child, depth: @depth+1, parent: self)
23
23
  when 'attribute'
24
24
  else
25
25
  raise "ERROR: unknown element: #{child.name}"
@@ -11,9 +11,9 @@ module Junoser
11
11
  @config ||= children.map {|child|
12
12
  case child.name
13
13
  when 'choice'
14
- Junoser::Xsd::Choice.new(child, depth: @depth+1)
14
+ Junoser::Xsd::Choice.new(child, depth: @depth+1, parent: self)
15
15
  when 'element'
16
- Junoser::Xsd::Element.new(child, depth: @depth+1)
16
+ Junoser::Xsd::Element.new(child, depth: @depth+1, parent: self)
17
17
  when 'any'
18
18
  'any'
19
19
  else
@@ -28,7 +28,15 @@ module Junoser
28
28
  when has_single_child_of?(Junoser::Xsd::Choice)
29
29
  child = config.first
30
30
  str = child.config.map(&:to_s).reject(&:empty?).join(",\n")
31
- format('c(', str, ')') unless str.empty?
31
+
32
+ return if str.empty?
33
+
34
+ # Assuming that <xsd:sequence> always has <xsd:complexType> as the parent
35
+ if parent.parent&.oneliner? && config.first.unbounded?
36
+ format('sc(', str, ')')
37
+ else
38
+ format('c(', str, ')')
39
+ end
32
40
  else
33
41
  str = config.map {|c| c.is_a?(String) ? format(OFFSET + c) : c.to_s }.reject(&:empty?).join(",\n")
34
42
  format('s(', str, ')')