require 'rexml/document' require 'rexml/parsers/sax2parser' require 'rexml/sax2listener' class Element attr_reader :parent, :name attr_accessor :children, :xmlbase def initialize parent, name, attrs @parent = parent @xmlbase = parent.xmlbase @children = [] @name = name @attrs = attrs end def log message, args={} if not @attrs args[:attr] = @name elsif not args[:element] args[:element] = @name elsif not args[:parent] args[:parent] = @name end @parent.send :log, message, args end def new_rule rule, name, attrs Rules[rule].new(self,name,attrs) end def attribute_rules name self.class.attribute_rules name end def element_rules *args self.class.element_rules *args end include REXML::SAX2Listener # attlistdecl(element, pairs, contents) # cdata(content) # characters(text) # comment(comment) # doctype(name, pub_sys, long_name, uri) # elementdecl(content) # end_document() # end_element(uri, localname, qname) # end_prefix_mapping(prefix) # entitydecl(content) # notationdecl(content) # processing_instruction(target, data) # start_document() # start_element(uri, localname, qname, attributes) # start_prefix_mapping(prefix, uri) # xmldecl(version, encoding, standalone) end class << Element def inherited sub; sub.class_eval {initialize}; end def initialize @element = {} @attribute = {} attribute "xml:base", :setxmlbase attribute "xml:lang", :iso639 end def element symbol, *decls prefix = "#{caller[0].scan /(\w+)\./}:" @element[prefix + symbol.to_s] = decls end def attribute symbol, *decls @attribute[symbol.to_s] = decls end def element_rules uri, localname, qname, attributes prefix = Xmlns[uri] if prefix @element["#{prefix}:#{localname}"] or [:undefined_element] else [:unknown_namespace] end end def attribute_rules name @attribute[name] or [:unexpected_attribute] end end ######################################################################## # Subclasses # ######################################################################## class TextElement < Element def initialize parent, name, attrs super @value = '' end def characters text @value += text end def cdata content @value += content end def end_element uri, localname, qname validate REXML::Text.unnormalize(@value) end def self.attribute_rules name @attribute[name] or [] end end class DataElement < TextElement def end_element uri, localname, qname if @value != @value.strip log :UnexpectedWhitespace @value.strip! end validate @value end end # stub class Cardinality < Element def element_rules uri, localname, qname, attributes [] end def validate value end end REQUIRED = Cardinality.clone MANY = Cardinality.clone class DiscriminatedUnion < Element def initialize parent, name, attrs super @rules = select(attrs).map {|rule| parent.new_rule(rule, name, attrs)} end REXML::SAX2Listener.public_instance_methods.each do |method| define_method(method) do |*args| @rules.each {|rule| rule.send(method, *args)} end end def element_rules *args @rules.collect {|rule| rule.element_rules(*args)}.flatten.uniq end end