ppovb5fc7/sdformat/tools/xmlschema.rb

303 lines
8.6 KiB
Ruby
Raw Permalink Normal View History

2019-03-25 11:01:43 +08:00
#!/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