File: xmlschema.rb

package info (click to toggle)
sdformat 9.3.0%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 5,708 kB
  • sloc: cpp: 42,166; python: 1,618; javascript: 704; ruby: 368; sh: 81; ansic: 37; makefile: 16
file content (302 lines) | stat: -rwxr-xr-x 8,813 bytes parent folder | download | duplicates (3)
1
2
3
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
134
135
136
137
138
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/usr/bin/env ruby

require "rexml/document"
require "optparse"

$path = nil

#################################################
# \brief A not very elegant way to convert to schema types
def xsdType(_type)
  if _type == "unsigned int"
    return "unsignedInt"
  elsif _type == "unsigned long"
    return "unsignedLog"
  elsif _type == "bool"
    return "boolean"
  else
    return _type
  end
end

#################################################
def isStdType(_type)
 return _type == "string" || _type == "int" || _type == "double" ||
   _type == "float" || _type == "bool" || _type == "char" ||
   _type == "unsigned int"
end

#################################################
def printElem(_file, _spaces, _elem)

  # this currently short-circuits the plugin.sdf copy_data element.
  if _elem.attributes["name"].nil?
    _file.printf("%*s<xsd:sequence>\n", _spaces-2, "")
    _file.printf("%*s<xsd:any minOccurs='0' maxOccurs='unbounded' processContents='lax'/>\n", _spaces, "")
    _file.printf("%*s</xsd:sequence>\n", _spaces-2, "")
    return
  end

  type = _elem.attributes["type"]
  if isStdType(type)
    type = "xsd:" + xsdType(type)
  end

  minOccurs = '0'
  maxOccurs = 'unbounded'
  if _elem.attributes["required"] == '0'
    minOccurs='0'
    maxOccurs='1'
  elsif _elem.attributes["required"] == '1'
    minOccurs='1'
    maxOccurs='1'
  elsif _elem.attributes["required"] == '+'
    minOccurs='1'
    maxOccurs='unbounded'
  elsif _elem.attributes["required"] == '*'
    minOccurs='0'
    maxOccurs='unbounded'
  end

  _file.printf("%*s<xsd:choice  minOccurs='%s' maxOccurs='%s'>\n",
               _spaces, "", minOccurs, maxOccurs)

  # Print the complex type with a name
  if type.nil? || type == ""

    _file.printf("%*s<xsd:element name='%s'>\n",
                 _spaces, "", _elem.attributes["name"])

    if !_elem.elements["description"].nil? &&
       !_elem.elements["description"].text.nil?
      printDocumentation(_file, _spaces+2, _elem.elements["description"].text)
    end

    _file.printf("%*s<xsd:complexType>\n", _spaces+2, "")

    _file.printf("%*s<xsd:choice maxOccurs='unbounded'>\n", _spaces+4, "")

    _elem.get_elements("element").each do |elem|
      printElem(_file, _spaces+6, elem)
    end
    _file.printf("%*s</xsd:choice>\n", _spaces+4, "")

    # Print the attributes for the complex type
    # Attributes must go at the end of the complex type.
    _elem.get_elements("attribute").each do |attr|
      printAttribute(_file, _spaces+4, attr);
    end

    _file.printf("%*s</xsd:complexType>\n", _spaces+2, "")
  else
    _file.printf("%*s<xsd:element name='%s' type='%s'>\n",
                 _spaces, "", _elem.attributes["name"], type)

    if !_elem.elements["description"].nil? &&
       !_elem.elements["description"].text.nil?
      printDocumentation(_file, _spaces+2, _elem.elements["description"].text)
    end
  end

  _file.printf("%*s</xsd:element>\n", _spaces, "")
  _file.printf("%*s</xsd:choice>\n", _spaces, "")

end

#################################################
def printDocumentation(_file, _spaces, _doc)
  _file.printf("%*s<xsd:annotation>\n", _spaces, "")

  _spaces += 2
  _file.printf("%*s<xsd:documentation xml:lang='en'>\n", _spaces, "")

  _spaces += 2
  _file.printf("%*s<![CDATA[%s]]>\n",_spaces, "", _doc);
  _spaces -= 2

  _file.printf("%*s</xsd:documentation>\n", _spaces, "")
  _spaces -= 2

  _file.printf("%*s</xsd:annotation>\n", _spaces, "")
end

#################################################
def printIncludeRef(_file, _spaces, _inc)
  path = File.join($path, _inc.attributes["filename"])
  doc = REXML::Document.new File.new(path)
  incElemName = doc.root.attributes['name']
  _file.printf("%*s<xsd:element ref='%s'/>\n", _spaces, "", incElemName)
end

#################################################
def printInclude(_file, _spaces, _attr)
  loc = "http://sdformat.org/schemas/"
  loc += _attr.attributes['filename'].sub("\.sdf","\.xsd")
  _file.printf("%*s<xsd:include schemaLocation='%s'/>\n", _spaces, "", loc)
end

#################################################
def printAttribute(_file, _spaces, _attr)
  name = _attr.attributes["name"]
  type = _attr.attributes["type"]
  use = ""
  default = ""

  if !_attr.attributes["required"].nil?
    if _attr.attributes["required"] == "1"
      use = "use='required'"
    elsif _attr.attributes["required"] == "0"
      use = "use='optional'"

      # Default is only valid if use is optional
      if !_attr.attributes["default"].nil?
        default="default='#{_attr.attributes["default"]}'"
      end
    end
  end

  if isStdType(type)
    type = "xsd:" + xsdType(type)
  end

  _file.printf("%*s<xsd:attribute name='%s' type='%s' %s %s>\n", _spaces,
               "", name, type, use, default)

  if !_attr.elements["description"].nil? &&
     !_attr.elements["description"].text.nil?
    printDocumentation(_file, _spaces+2, _attr.elements["description"].text)
  end
  _file.printf("%*s</xsd:attribute>\n", _spaces, "")
end

#################################################
# \brief Print the complete schema for an element into a file.
# \param[in] _file File pointer in which to print the schema.
# \param[in] _spaces Number of spaces to prepend to each line.
# \param[in] _elem The SDF element to convert to an xml schema.
def printXSD(_file, _spaces, _elem)

  if !_elem.elements["description"].nil? &&
     !_elem.elements["description"].text.nil?
    printDocumentation(_file, _spaces, _elem.elements["description"].text)
  end

  _file.printf("%*s<xsd:include schemaLocation='http://sdformat.org/schemas/types.xsd'/>\n", _spaces, "")

  # Print the inclues for the complex type
  # The includes must appear first
  _elem.get_elements("include").each do |inc|
    printInclude(_file, _spaces, inc);
  end

  if _elem.get_elements("element").size > 0 ||
     _elem.get_elements("attribute").size > 0 ||
     _elem.get_elements("include").size > 0

    # Print the complex type with a name
    _file.printf("%*s<xsd:element name='%s'>\n", _spaces, "",
                 _elem.attributes["name"])
    _file.printf("%*s<xsd:complexType>\n", _spaces+2, "")

    if _elem.attributes['name'] != "plugin" &&
      (_elem.get_elements("element").size > 0 ||
       _elem.get_elements("include").size > 0)
      _file.printf("%*s<xsd:choice maxOccurs='unbounded'>\n", _spaces+4, "")
    end

    # Print all the child elements
    _elem.get_elements("element").each do |elem|
      printElem(_file, _spaces+6, elem);
    end

    # Print all the included sdf's root elements
    _elem.get_elements("include").each do |inc|
      printIncludeRef(_file, _spaces+6, inc);
    end
    if _elem.attributes['name'] != "plugin" &&
      (_elem.get_elements("element").size > 0 ||
       _elem.get_elements("include").size > 0)
      _file.printf("%*s</xsd:choice>\n", _spaces+4, "")
    end

    # Print the attributes for the complex type
    # Attributes must go at the end of the complex type.
    _elem.get_elements("attribute").each do |attr|
      printAttribute(_file, _spaces+4, attr);
    end

    # Close the complex type
    _file.printf("%*s</xsd:complexType>\n", _spaces+2, "")
    _file.printf("%*s</xsd:element>\n", _spaces, "")
  else
    type = _elem.attributes["type"]

    if isStdType(type)
      type = "xsd:" + type
    end

    if !type.nil?
      type = "type='" + type + "'"
    end

    _file.printf("%*s<xsd:element name='%s' %s/>\n", _spaces, "",
                 _elem.attributes["name"], type)
  end
end


infile = nil
outdir = nil

opt_parser = OptionParser.new do |o|
  o.on("-i", "--in [path]", String,
       "SDF file to compile") {|path| infile = path}
  o.on("-o", "--out [path]", String,
       "Output directory for source and header files") {|path| outdir = path}
  o.on("-s", "--sdf [path]", String,
       "Directory containing all the SDF files") {|path| $path = path}
  o.on("-h", "--help", "Display this help message") do
    puts opt_parser
    exit
  end
end
opt_parser.parse!

if infile.nil?
  puts "Missing option -i."
  exit
elsif !File.exists?(infile)
  puts "Input file[#{infile}] does not exist\n"
  exit
end

if $path.nil?
  puts "Missing option -s."
  exit
elsif !Dir.exists?($path)
  puts "SDF source dir[#{$path}] does not exist\n"
  exit
end

if outdir.nil?
  puts "Missing output directory, option -o."
  exit
elsif !Dir.exists?(outdir)
  Dir.mkdir(outdir)
end

doc = REXML::Document.new File.new(infile)

spaces = 2
doc.elements.each_with_index("element") do |elem, i|
  out_xsd = infile.split("/").last.sub("\.sdf","\.xsd")
  file = File.open(File.join(outdir, out_xsd), "w")

  file.print("<?xml version='1.0' encoding='UTF-8'?>\n")
  file.print("<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>\n")

  printXSD(file, spaces, elem)

  file.print("</xsd:schema>\n")
  file.close()
end