lib/xsd/document.rb
module Xsd
BUILD_IN_TYPES =
{
'type:http://www.w3.org/2001/XMLSchema:decimal' => { 'type' => 'number' },
'type:http://www.w3.org/2001/XMLSchema:float' => { 'type' => 'number' },
'type:http://www.w3.org/2001/XMLSchema:double' => { 'type' => 'number' },
'type:http://www.w3.org/2001/XMLSchema:integer' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:positiveInteger' => { 'type' => 'integer', 'minimum' => 0, 'exclusiveMinimum' => true },
'type:http://www.w3.org/2001/XMLSchema:negativeInteger' => { 'type' => 'integer', 'maximum' => 0, 'exclusiveMaximum' => true },
'type:http://www.w3.org/2001/XMLSchema:nonPositiveInteger' => { 'type' => 'integer', 'maximum' => 0 },
'type:http://www.w3.org/2001/XMLSchema:nonNegativeInteger' => { 'type' => 'integer', 'minimum' => 0 },
'type:http://www.w3.org/2001/XMLSchema:long' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:int' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:short' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:byte' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:unsignedLong' => { 'type' => 'integer', 'minimum' => 0 },
'type:http://www.w3.org/2001/XMLSchema:unsignedInt' => { 'type' => 'integer', 'minimum' => 0 },
'type:http://www.w3.org/2001/XMLSchema:unsignedShort' => { 'type' => 'integer', 'minimum' => 0 },
'type:http://www.w3.org/2001/XMLSchema:unsignedByte' => { 'type' => 'integer', 'minimum' => 0 },
'type:http://www.w3.org/2001/XMLSchema:date' => { 'type' => 'string', 'format' => 'date' },
'type:http://www.w3.org/2001/XMLSchema:dateTime' => { 'type' => 'string', 'format' => 'date-time' },
'type:http://www.w3.org/2001/XMLSchema:gYearMonth' => { 'type' => 'string', 'format' => 'date' },
'type:http://www.w3.org/2001/XMLSchema:gYear' => { 'type' => 'string', 'format' => 'date' },
'type:http://www.w3.org/2001/XMLSchema:duration' => { 'type' => 'string', 'pattern' => 'P([0-9]*Y)?([0-9]*M)?([0-9]*D)?(T([0-9]*H)?([0-9]*M)?([0-9]*S)?)?' },
'type:http://www.w3.org/2001/XMLSchema:time' => { 'type' => 'string', 'format' => 'time' },
'type:http://www.w3.org/2001/XMLSchema:gMonthDay' => { 'type' => 'string', 'format' => 'date' },
'type:http://www.w3.org/2001/XMLSchema:gMonth' => { 'type' => 'string', 'format' => 'date' },
'type:http://www.w3.org/2001/XMLSchema:gDay' => { 'type' => 'string', 'format' => 'date' },
'type:http://www.w3.org/2001/XMLSchema:string' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:normalizedString' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:token' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:language' => { 'type' => 'string', "minLength" => 1 },
'type:http://www.w3.org/2001/XMLSchema:NMTOKEN' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:NMTOKENS' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:Name' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:NameNC' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:ID' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:IDREF' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:IDREFS' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:ENTITY' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:ENTITIES' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:QName' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:boolean' => { 'type' => 'boolean' },
'type:http://www.w3.org/2001/XMLSchema:hexBinary' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:base64Binary' => { 'type' => 'integer' },
'type:http://www.w3.org/2001/XMLSchema:anyURI' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:notation' => { 'type' => 'string' },
'type:http://www.w3.org/2001/XMLSchema:anyType' => {}
}.freeze
class Document < Nokogiri::XML::SAX::Document
attr_reader :uri
attr_reader :name_prefix
attr_reader :schema
def initialize(uri, str_doc, name_prefix='')
@uri = uri
@name_prefix = name_prefix
@stack = [:floor]
parser = Nokogiri::XML::SAX::Parser.new(self)
parser.parse(str_doc)
end
def error(string)
raise Exception.new("Invalid xml schema format: #{string}")
end
def start_element(name, attributes = [])
unless @xsd_tag
@xsd_tag = 'http://www.w3.org/2001/XMLSchema'
attr = attributes.detect { |attr| attr[0] =~ /\Axmlns:/ && attr[1] == 'http://www.w3.org/2001/XMLSchema' }
if attr
@xsd_tag = attr[0].from(attr[0].index(':') + 1)
end
end
name = "#{name.from(@xsd_tag.length + 1)}" if name.start_with?("#{@xsd_tag}:")
primary_method = "#{name}_start".to_sym
push process_element_message(primary_method, :start_element_tag, name, attributes)
end
def end_element(name)
name = "#{name.from(@xsd_tag.length + 1)}" if name.start_with?("#{@xsd_tag}:")
primary_method = "#{name}_end".to_sym
if (element = push process_element_message(primary_method, :end_element_tag, name)) &&
top.respond_to?(parent_callback_method = "when_#{primary_method}".to_sym)
top.send(parent_callback_method, element)
end
end
def schema_start(attributes = [])
@schema = Schema.new(parent: top_if_available, attributes: attributes, name_prefix: @name_prefix, document: self)
end
[Annotation, Documentation, Attribute, AttributeGroup, Element, ComplexType, SimpleType].each do |tag_type|
class_eval("def #{tag_type.tag_name}_start(attributes = [])
#{tag_type}.new(parent: top_if_available, attributes: attributes)
end")
end
def characters(string)
if (current_tag = top_if_available).respond_to?(:characters)
current_tag.characters(string)
end
end
private
def process_element_message(primary_method, alternative_method, name, attributes = nil)
if top.respond_to?(primary_method)
return top.send(primary_method, attributes) if attributes
return top.send(primary_method)
elsif top_available? && top.respond_to?(alternative_method)
return top.send(alternative_method, name, attributes) if attributes
return top.send(alternative_method, name)
elsif self.respond_to?(primary_method)
return self.send(primary_method, attributes) if attributes
return self.send(primary_method)
end
nil
end
def push(obj)
obj == :pop ? @stack.pop : (@stack << obj if obj)
end
def pop
@stack.last == :floor ? self : @stack.pop
end
def pop!
pop while top_available?
@stack.pop
@stack << :floor if @stack.empty?
end
def top
@stack.last == :floor ? self : @stack.last
end
def top_if_available
top_available? ? top : nil
end
def top_available?
@stack.last != :floor
end
end
end