Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial RXSD commit

  • Loading branch information...
commit 96344852bb70e0115e25aaecb9b9e32a285dfa2d 0 parents
@movitto movitto authored
Showing with 2,940 additions and 0 deletions.
  1. +8 −0 COPYING
  2. +165 −0 LICENSE
  3. +19 −0 README
  4. +29 −0 Rakefile
  5. +13 −0 TODO
  6. +19 −0 lib/rxsd.rb
  7. +156 −0 lib/rxsd/builder.rb
  8. +65 −0 lib/rxsd/common.rb
  9. +16 −0 lib/rxsd/exceptions.rb
  10. +74 −0 lib/rxsd/libxml.rb
  11. +30 −0 lib/rxsd/loader.rb
  12. +117 −0 lib/rxsd/parser.rb
  13. +45 −0 lib/rxsd/resolver.rb
  14. +38 −0 lib/rxsd/translator.rb
  15. +107 −0 lib/rxsd/xsd/attribute.rb
  16. +82 −0 lib/rxsd/xsd/attribute_group.rb
  17. +99 −0 lib/rxsd/xsd/choice.rb
  18. +79 −0 lib/rxsd/xsd/complex_content.rb
  19. +123 −0 lib/rxsd/xsd/complex_type.rb
  20. +136 −0 lib/rxsd/xsd/element.rb
  21. +119 −0 lib/rxsd/xsd/extension.rb
  22. +92 −0 lib/rxsd/xsd/group.rb
  23. +91 −0 lib/rxsd/xsd/list.rb
  24. +166 −0 lib/rxsd/xsd/restriction.rb
  25. +114 −0 lib/rxsd/xsd/schema.rb
  26. +95 −0 lib/rxsd/xsd/sequence.rb
  27. +78 −0 lib/rxsd/xsd/simple_content.rb
  28. +92 −0 lib/rxsd/xsd/simple_type.rb
  29. +19 −0 test/all_tests.rb
  30. +92 −0 test/builder_test.rb
  31. +18 −0 test/loader_test.rb
  32. +412 −0 test/parser_test.rb
  33. +69 −0 test/resolver_test.rb
  34. +63 −0 test/translator_test.rb
8 COPYING
@@ -0,0 +1,8 @@
+RXSD is free software, you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License
+as published by the Free Software Foundation, either version 3
+of the License, or (at your option) any later version.
+
+You should have received a copy of the the GNU Lesser
+General Public License, along with RXSD. If not, see
+<http://www.gnu.org/licenses/>
165 LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
19 README
@@ -0,0 +1,19 @@
+RXSD - XSD / Ruby Translator
+Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+See COPYING for the License of this software
+
+RXSD is a library that translates XSD XML Schema Definitions into Ruby Classes
+on the fly. It is able to read XSD resources and use them to define Ruby
+classes in memory or string class definitions to be written to the filesystem
+
+RXSD implements a full XSD parser that not only defines the various xsd schema
+classes, parsing them out of a XSD file, but translates them into a
+meta-class heirarchy, for use in subsequent transformations. The builder interface
+can easily be extended to output any format one could want including classes
+in other languages (Python, C++, Java, etc), other XML formats, etc.
+
+RXSD also parses XML conforming to a XSD schema, and instantiates objects
+corresponding to the XSD classes created. Furthermore, RXSD will work with
+existing class definitions resulting in a quick way to map XSD to Ruby constructs,
+letting you define the schema features that you need, and autogenerting handlers
+to the others.
29 Rakefile
@@ -0,0 +1,29 @@
+# rxsd project Rakefile
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+#task :default => :test
+
+task(:test) do
+ desc "Run tests"
+ require 'test/all_tests'
+end
+
+task :rdoc do
+ desc "Create RDoc documentation"
+ system "rdoc --title 'rxsd documentation' lib/"
+end
+
+task :create_gem do
+ desc "Create a new gem"
+ system "gem build rxsd.gemspec"
+end
+
+task :dist do
+ desc "Create a source tarball"
+ system "mkdir ruby-rxsd-0.1.0 && \
+ cp -R conf/ bin/ db/ lib/ test/ ruby-rxsd-0.1.0/ && \
+ tar czvf rxsd.tgz ruby-rxsd-0.1.0 && \
+ rm -rf ruby-rxsd-0.1.0"
+end
13 TODO
@@ -0,0 +1,13 @@
+RXSD is a work in progress and is not current stable.
+
+Various features left to be implemented
+
+ * LOTS of bug fixing (expanding the test suite in the process)
+ * Support for missing XSD constructs:
+ - all / annotation / any / anyAttribute / appInfo / documentation / field /
+ import / include / key / keyref / notation / redefine / selector / union / unique
+ * Make Builder interface more generic, w/ subclasses for ruby classes, defintions, whatever else
+ * Means to autocovert XSD conforming XML into instances of classes created
+ * C/C++ rewrite (?)
+ * More design cleanup / doing things the "right" way
+ (make things more plugable, including additional XML backend support)
19 lib/rxsd.rb
@@ -0,0 +1,19 @@
+# include all rxsd modules
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+lib = File.dirname(__FILE__)
+
+require 'rubygems'
+require 'motel'
+
+require lib + '/rxsd/common'
+require lib + '/rxsd/libxml'
+require lib + '/rxsd/loader'
+require lib + '/rxsd/resolver'
+require lib + '/rxsd/parser'
+require lib + '/rxsd/builder'
+require lib + '/rxsd/translator'
+
+Dir[lib + '/rxsd/xsd/*.rb'].each { |xsd_module| require xsd_module }
156 lib/rxsd/builder.rb
@@ -0,0 +1,156 @@
+# RXSD builder
+#
+# responsible for providing simple interface to build ruby classes
+# and output them in various formats, eg verbatim, text, etc
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+
+# build ruby class from specified parameters
+class ClassBuilder
+ # actual ruby class built
+ attr_accessor :klass
+
+ # name of ruby class to build
+ attr_accessor :klass_name
+
+ # class builder corresponding to associated type where approprate
+ attr_accessor :associated_builder
+
+ # class builder corresponding to base type where approprate
+ attr_accessor :base_builder
+
+ # array of class builders for all attributes
+ attr_accessor :attribute_builders
+
+ # name of the attribute which this class represents, for use in accessor construction
+ attr_accessor :attribute_name
+
+ # create a new class builder w/ specified args
+ def initialize(args = {})
+ @klass = args[:klass]
+ @klass_name = args[:klass_name]
+
+ if args.has_key? :base_builder
+ @base_builder = args[:base_builder]
+ elsif args.has_key?(:base) && !args[:base].nil?
+ @base_builder = ClassBuilder.new :klass => args[:base]
+ end
+
+ @attribute_builders = []
+ end
+
+ # build Ruby Class instance using this builder
+ def build_class
+ # if buildin, just return it
+ return @klass if Parser.is_builtin? @klass
+
+ # need the class name to build class
+ return nil if @klass_name.nil?
+
+ Logger.debug "building class #{@klass}/#{@klass_name} from xsd"
+
+ # determine object's superclass, creating it if need be
+ superclass = Object
+ unless @base_builder.nil?
+ if @base_builder.klass.nil?
+ @base_builder.build_class
+ end
+ superclass = @base_builder.klass
+ end
+
+ # create object
+ unless Object.constants.include? @klass_name
+ Object.const_set(@klass_name, Class.new(superclass))
+ end
+ @klass = @klass_name.constantize
+
+ # define accessors for attributes
+ @attribute_builders.each { |atb|
+ unless atb.nil?
+ att_name = nil
+ if !atb.attribute_name.nil?
+ att_name = atb.attribute_name.underscore
+ elsif !atb.klass_name.nil?
+ att_name = atb.klass_name.underscore
+ elsif !atb.klass.nil?
+ att_name = atb.klass.to_s.underscore
+ end
+
+ @klass.send :attr_accessor, att_name.intern unless att_name.nil?
+ end
+ }
+
+ Logger.debug "class #{@klass} built, returning"
+ return @klass
+ end
+
+ # build Ruby Class definition using this builder
+ def build_definition
+ return "class #{@klass.to_s}\nend" if Parser.is_builtin? @klass
+
+ # need the class name to build class
+ return nil if @klass_name.nil?
+
+ Logger.debug "building definition for #{@klass}/#{@klass_name} from xsd"
+
+ # defined class w/ base
+ superclass = "Object"
+ unless @base_builder.nil?
+ if ! @base_builder.klass_name.nil?
+ superclass = @base_builder.klass_name
+ elsif ! @base_builder.klass.nil?
+ superclass = @base_builder.klass.to_s
+ end
+ end
+ res = "class " + @klass_name + " < " + superclass + "\n"
+
+ # define accessors for attributes
+ @attribute_builders.each { |atb|
+ unless atb.nil?
+ att_name = nil
+ if !atb.attribute_name.nil?
+ att_name = atb.attribute_name.underscore
+ elsif !atb.klass_name.nil?
+ att_name = atb.klass_name.underscore
+ else
+ att_name = atb.klass.to_s.underscore
+ end
+
+ res += "attr_accessor :#{att_name}\n"
+ end
+ }
+ res += "end"
+
+ Logger.debug "definition #{res} built, returning"
+ return res
+ end
+
+ # helper method to get all associated class builders
+ def associated
+ builders = []
+
+ unless @base_builder.nil? || builders.include?(@base_builder)
+ builders.push @base_builder
+ builders += @base_builder.associated
+ end
+
+ unless @associated_builder.nil? || builders.include?(@associated_builder)
+ builders.push @associated_builder
+ builders += @associated_builder.associated
+ end
+
+ @attribute_builders.each { |ab|
+ unless ab.nil? || builders.include?(ab)
+ builders.push ab
+ builders += ab.associated
+ end
+ }
+
+ return builders
+ end
+
+end # class ClassBuilder
+end # module RXSD
65 lib/rxsd/common.rb
@@ -0,0 +1,65 @@
+# Things that don't fit elsewhere
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+# we make use of the activesupport inflector
+#require 'active_support'
+
+# logger support
+require 'logger'
+
+module RXSD
+ # Logger helper class
+ class Logger
+ private
+ LOG_LEVEL = ::Logger::FATAL # FATAL ERROR WARN INFO DEBUG
+
+ def self._instantiate_logger
+ unless defined? @@logger
+ @@logger = ::Logger.new(STDOUT)
+ @@logger.level = LOG_LEVEL
+ end
+ end
+
+ public
+ def self.method_missing(method_id, *args)
+ _instantiate_logger
+ @@logger.send(method_id, args)
+ end
+ def self.logger
+ _instantiate_logger
+ @@logger
+ end
+ end
+end
+
+# read entire file into string
+def File.read_all(path)
+ File.open(path, 'rb') {|file| return file.read }
+end
+
+# write contents of file from string
+def File.write(path, str)
+ File.open(path, 'wb') {|file| file.write str }
+end
+
+# convert string to boolean
+class String
+ def to_b
+ return true if self == true || self =~ /^true$/i
+ return false if self == false || self.nil? || self =~ /^false$/i
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
+ end
+end
+
+# ruby doesn't define Boolean class, so we do
+# dispatching to TrueClass / FalseClass
+class Boolean
+end
+
+# ruby doesn't define Char class, so we do
+# dispatching to simple string
+class Char
+end
+
16 lib/rxsd/exceptions.rb
@@ -0,0 +1,16 @@
+# RXSD exceptions
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+require 'uri' # use uri to parse sources
+
+module RXSD
+module Exceptions
+
+# thrown when specified resource uri is invalid
+class InvalidResourceUri
+end
+
+end # module Exceptions
+end # module RXSD
74 lib/rxsd/libxml.rb
@@ -0,0 +1,74 @@
+# libxml adapter
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+require 'rubygems'
+require 'libxml' # based on libxml
+
+# some additions to libxml xml node interface
+class LibXML::XML::Node
+
+ # This would be an easy place to make xml backend configurable,
+ # eg use rexml as an alternative.
+ # Just override all the following methods:
+
+ #def name
+ # should return name of node, eg <foo> => "foo"
+ #end
+
+ # return hash of attribute name / values
+ def attrs
+ attributes.to_h
+ end
+
+ # def parent?
+ # # should return bool if node has a parent
+ #end
+
+ #def parent
+ # # should return this nodes's parent, if any
+ #end
+
+ # def children
+ # # should return children nodes
+ #end
+
+ # def text?
+ # # should return bool if node only contains text
+ #end
+
+ # return root node
+ def root
+ parent? ? parent.root : self
+ end
+
+ # provide accessor interface to related obj, in our case related xsd obj
+ attr_accessor :related
+
+ # instantiate all children of provided class type
+ def children_objs(klass)
+ elements = []
+ children.find_all { |c| c.name == klass.tag_name }.each { |c|
+ elements.push(klass.from_xml(c)) }
+ return elements
+ end
+
+ # instantiate first child of provided class type
+ def child_obj(klass)
+ return children_objs(klass)[0]
+ end
+
+ # return 'value' attribute of all children w/ specified tag
+ def child_values(tag_name)
+ values = []
+ children.find_all { |c| c.name == tag_name }.each { |c| values.push(c.attrs['value']) }
+ return values
+ end
+
+ # return 'value' attribute of first childw/ specified tag
+ def child_value(tag_name)
+ return child_values(tag_name)[0]
+ end
+
+end
30 lib/rxsd/loader.rb
@@ -0,0 +1,30 @@
+# RXSD resource loader
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+require 'uri' # use uri to parse sources
+
+module RXSD
+
+# loads resources from uris
+class Loader
+
+ # loads and return text resource from specified source uri
+ def self.load(source_uri)
+ Logger.info "loading resource from uri #{source_uri}"
+ data = nil
+ uri = URI.parse(source_uri)
+ if uri.scheme == "file"
+ data = File.read_all uri.path
+ # elsif FIXME support other uri types
+ end
+
+ return data
+
+ rescue URI::InvalidURIError
+ raise Exceptions::InvalidResourceUri
+ end
+
+end # class loader
+end # module RXSD
117 lib/rxsd/parser.rb
@@ -0,0 +1,117 @@
+# xml / xsd parsers
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+
+# provides class methods to parse xsd and xml data
+class Parser
+ private
+ def initialize
+ end
+
+ public
+
+ # parse xsd specified by uri or in raw data form
+ # args should be a hash w/ optional keys:
+ # * :uri location which to load resource from
+ # * :raw raw data which to parse
+ def self.parse_xsd(args)
+ data = Loader.load(args[:uri]) unless args[:uri].nil?
+ data = args[:raw] unless args[:raw].nil?
+ Logger.debug "parsing following xsd: #{data}"
+
+ # FIXME validate against xsd's own xsd
+ doc = LibXML::XML::Document.string data
+ schema = XSD::Schema.from_xml doc.root
+
+ Logger.debug "parsed xsd, resolving relationships"
+ Resolver.resolve_nodes schema
+
+ Logger.debug "xsd parsing complete"
+ return schema
+ end
+
+ def parse_xml(xml, xsd)
+ end
+
+ # return true is specified class is builtin, else false
+ def self.is_builtin?(builtin_class)
+ [Array, String, Boolean, Char, Float, Integer].include? builtin_class
+ end
+
+ # return ruby class corresponding to builting type
+ def self.parse_builtin_type(builtin_type_name)
+ res = nil
+
+ case builtin_type_name
+ when "xs:string":
+ res = String
+ when "xs:boolean":
+ res = Boolean
+ when "xs:decimal":
+ res = Float
+ when "xs:float":
+ res = Float
+ when "xs:double":
+ res = Float
+ when "xs:duration":
+ when "xs:dateTime":
+ when "xs:date":
+ when "xs:gYearMonth":
+ when "xs:gYear":
+ when "xs:gMonthDay":
+ when "xs:gDay":
+ when "xs:gMonth":
+ when "xs:hexBinary":
+ when "xs:base64Binary":
+ when "xs:anyURI":
+ when "xs:QName":
+ when "xs:NOTATION":
+ when "xs:normalizedString"
+ when "xs:token"
+ when "xs:language"
+ when "xs:NMTOKEN"
+ when "xs:NMTOKENS"
+ when "xs:Name"
+ when "xs:NCName"
+ when "xs:ID"
+ when "xs:IDREF"
+ when "xs:IDREFS"
+ when "xs:ENTITY"
+ when "xs:ENTITIES"
+ when "xs:integer"
+ res = Integer
+ when "xs:nonPositiveInteger"
+ res = Integer
+ when "xs:negativeInteger"
+ res = Integer
+ when "xs:long"
+ res = Integer
+ when "xs:int"
+ res = Integer
+ when "xs:short"
+ res = Integer
+ when "xs:byte"
+ res = Char
+ when "xs:nonNegativeInteger"
+ res = Integer
+ when "xs:unsignedLong"
+ res = Integer
+ when "xs:unsignedInt"
+ res = Integer
+ when "xs:unsignedShort"
+ res = Integer
+ when "xs:unsignedByte"
+ res = Char
+ when "xs:positiveInteger"
+ res = Integer
+ end
+
+ return res
+ end
+
+end
+
+end
45 lib/rxsd/resolver.rb
@@ -0,0 +1,45 @@
+# RXSD resolver
+#
+# resolves hanging node relationships and provides overall node access
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+
+# resolves
+class Resolver
+
+ # return array of node objs for all nodes
+ # underneath given node_obj (inclusive)
+ def self.node_objects(node_obj, args = {})
+ if args.has_key? :node_objs
+ node_objs = args[:node_objs]
+ elsif node_obj.nil?
+ node_objs = []
+ else
+ node_objs = [node_obj]
+ end
+
+ unless node_obj.nil?
+ node_obj.children.each{ |noc|
+ unless noc.nil? || node_objs.include?(noc)
+ node_objs.push noc
+ node_objs = node_objects(noc, :node_objs => node_objs) # might be better to do a breadth first traversal instead?
+ end
+ }
+ end
+
+ node_objs
+ end
+
+ # resolves hanging node relationships for specified schema
+ def self.resolve_nodes(schema)
+ node_objs = node_objects(schema)
+ node_objs.each { |no|
+ no.resolve(node_objs)
+ }
+ end
+
+end # class Resolver
+end # module RXSD
38 lib/rxsd/translator.rb
@@ -0,0 +1,38 @@
+# RXSD translator
+#
+# transaltes xsd <-> ruby classes & xml <-> instances
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+
+# Extend XSD Schema Interface to
+# translate xsd/xml to / from ruby classes/objects
+module XSD
+class Schema
+
+ # translates schema and all child entities to instances of specified output type.
+ # output_type may be one of
+ # * :ruby_classes
+ # * :ruby_definitions
+ def to(output_type)
+ cbs = to_class_builders.collect { |cb| cb.associated.push cb }.flatten # FIXME filter duplicates
+ results = []
+ cbs.each { |cb|
+ case(output_type)
+ when :ruby_classes
+ cl = cb.build_class
+ results.push cl unless results.include? cl
+ when :ruby_definitions
+ df = cb.build_definition
+ results.push df unless results.include? df
+ end
+ }
+ return results
+ end
+
+end # class Schema
+
+end # module XSD
+end # module RXSD
107 lib/rxsd/xsd/attribute.rb
@@ -0,0 +1,107 @@
+# The XSD Attribute definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Attribute defintion
+# http://www.w3schools.com/Schema/el_attribute.asp
+class Attribute
+
+ # attribute attributes
+ attr_accessor :id, :name, :use, :form, :default, :fixed, :ref, :type
+
+ # attribute children
+ attr_accessor :simple_type
+
+ # attribute parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "attribute"
+ end
+
+ # return xsd node info
+ def info
+ "attribute id: #{@id} name: #{@name} type: #{@type.class} ref: #{ref.nil? ? "" : ref.name} "
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @simple_type unless @simple_type.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the attribute
+ def self.from_xml(node)
+ attribute = Attribute.new
+ attribute.parent = node.parent.related
+ node.related = attribute
+
+ # TODO attribute attributes: | anyAttributes
+ attribute.id = node.attrs["id"]
+ attribute.name = node.attrs["name"]
+ attribute.use = node.attrs["use"]
+
+ attribute.form = node.attrs.has_key?("form") ?
+ node.attrs["form"] : node.parent.attrs["attributeFormDefault"]
+
+ attribute.default = node.attrs["default"]
+ attribute.fixed = node.attrs["fixed"]
+
+ attribute.ref = node.attrs["ref"]
+
+ if node.children.find { |c| c.name == SimpleType.tag_name }.nil?
+ attribute.type = node.attrs["type"]
+ else
+ attribute.simple_type = node.child_obj SimpleType
+ end
+
+ return attribute
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @type.nil?
+ builtin = Parser.parse_builtin_type @type
+ @type = !builtin.nil? ? builtin : node_objs.find { |no| no.class == SimpleType && no.name == @type }
+ end
+
+ unless @ref.nil?
+ @ref = node_objs.find { |no| no.class == Attribute && no.name == @ref }
+ end
+ end
+
+ # convert complex type to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ @class_builder = nil
+ if !@ref.nil?
+ @class_builder = @ref.to_class_builder
+
+ elsif !@type.nil?
+ if @type.class == SimpleType
+ @class_builder = @type.to_class_builder
+ else
+ @class_builder = ClassBuilder.new :klass => @type
+ end
+
+ elsif !@simple_type.nil?
+ @class_builder = @simple_type.to_class_builder
+
+ end
+
+ @class_builder.attribute_name = @name unless @class_builder.nil?
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
82 lib/rxsd/xsd/attribute_group.rb
@@ -0,0 +1,82 @@
+# The XSD AttributeGroup definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD AttributeGroup defintion
+# http://www.w3schools.com/Schema/el_attributegroup.asp
+class AttributeGroup
+
+ # attribute group attributes
+ attr_accessor :id, :name, :ref
+
+ # attribute group children
+ attr_accessor :attributes, :attribute_groups
+
+ # attribute group parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "attributeGroup"
+ end
+
+ # return xsd node info
+ def info
+ "attributeGroup id: #{@id} name: #{@name} ref: #{ref.nil? ? "" : ref.name} "
+ end
+
+ # returns array of all children
+ def children
+ @attributes + @attribute_groups
+ end
+
+ # node passed in should be a xml node representing the attribute group
+ def self.from_xml(node)
+ attribute_group = AttributeGroup.new
+ attribute_group.parent = node.parent.related
+ node.related = attribute_group
+
+ # TODO attribute group attributes: | anyAttributes
+ attribute_group.id = node.attrs["id"]
+ attribute_group.name = node.attrs["name"]
+ attribute_group.ref = node.attrs["ref"]
+
+ # TODO attribute group children: | anyAttribute
+ attribute_group.attributes = node.children_objs Attribute
+ attribute_group.attribute_groups = node.children_objs AttributeGroup
+
+ return attribute_group
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @ref.nil?
+ @ref = node_objs.find { |no| no.class == AttributeGroup && no.name == @ref }
+ end
+ end
+
+ # convert attribute group to array of class builders
+ def to_class_builders
+ unless defined? @class_builders
+ @class_builders = []
+ @attributes.each { |att|
+ @class_builders.push att.to_class_builder
+ }
+ @attribute_groups.each { |atg|
+ atg.to_class_builders.each { |atcb|
+ @class_builders.push atcb
+ }
+ }
+ end
+
+ return @class_builders
+ end
+
+end
+
+end # module XSD
+end # module RXSD
99 lib/rxsd/xsd/choice.rb
@@ -0,0 +1,99 @@
+# The XSD Choice definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Choice defintion
+# http://www.w3schools.com/Schema/el_choice.asp
+class Choice
+
+ # choice attributes
+ attr_accessor :id, :maxOccurs, :minOccurs
+
+ # choice children
+ attr_accessor :elements, :groups, :choices, :sequences
+
+ # choice parent
+ attr_accessor :choice
+
+ # choice parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "choice"
+ end
+
+ # return xsd node info
+ def info
+ "choice id: #{@id}"
+ end
+
+ # returns array of all children
+ def children
+ @elements + @groups + @choices + @sequences
+ end
+
+ # node passed in should be a xml node representing the group
+ def self.from_xml(node)
+ choice = Choice.new
+ choice.parent = node.parent.related
+ node.related = choice
+
+ # TODO choice attributes: | anyAttributes
+ choice.id = node.attrs["id"]
+
+ choice.maxOccurs = node.attrs.has_key?("maxOccurs") ?
+ (node.attrs["maxOccurs"] == "unbounded" ? "unbounded" : node.attrs["maxOccurs"].to_i) : 1
+ choice.minOccurs = node.attrs.has_key?("minOccurs") ?
+ (node.attrs["minOccurs"] == "unbounded" ? "unbounded" : node.attrs["minOccurs"].to_i) : 1
+
+ # TODO choice children: | any
+ choice.elements = node.children_objs Element
+ choice.groups = node.children_objs Group
+ choice.choices = node.children_objs Choice
+ choice.sequences = node.children_objs Sequence
+
+ return choice
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ end
+
+ # convert choice to array of class builders
+ def to_class_builders
+ # FIXME enforce "only one attribute must be set"
+
+ unless defined? @class_builders
+ @class_builders = []
+ @elements.each { |e|
+ @class_builders.push e.to_class_builder
+ }
+ @groups.each { |g|
+ g.to_class_builders { |gcb|
+ @class_builders.push gcb
+ }
+ }
+ @choices.each { |c|
+ c.to_class_builders { |ccb|
+ @class_builders.push ccb
+ }
+ }
+ @sequences.each { |s|
+ s.to_class_builders { |scb|
+ @class_builders.push scb
+ }
+ }
+ end
+
+ return @class_builders
+ end
+
+end
+
+end # module XSD
+end # module RXSD
79 lib/rxsd/xsd/complex_content.rb
@@ -0,0 +1,79 @@
+# The XSD ComplexContent definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD ComplexContent defintion
+# http://www.w3schools.com/Schema/el_complexcontent.asp
+class ComplexContent
+
+ # complex content attributes
+ attr_accessor :id, :mixed
+
+ # complex content children
+ attr_accessor :restriction, :extension
+
+ # complex content parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "complexContent"
+ end
+
+ # return xsd node info
+ def info
+ "complexContent id: #{@id}"
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @restriction unless @restriction.nil?
+ c.push @extension unless @extension.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the group
+ def self.from_xml(node)
+ complex_content = ComplexContent.new
+ complex_content.parent = node.parent.related
+ node.related = complex_content
+
+ # TODO group attributes: | anyAttributes
+ complex_content.id = node.attrs["id"]
+ complex_content.mixed = node.attrs.has_key?("mixed") ? node.attrs["mixed"].to_b : false
+
+ complex_content.restriction = node.child_obj Restriction
+ complex_content.extension = node.child_obj Extension
+
+ return complex_content
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ end
+
+ # convert complex content to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ # dispatch to child restriction/extension
+ @class_builder = nil
+
+ if !@restriction.nil?
+ @class_builder = @restriction.to_class_builder
+ elsif !@extension.nil?
+ @class_builder = @extension.to_class_builder
+ end
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
123 lib/rxsd/xsd/complex_type.rb
@@ -0,0 +1,123 @@
+# The XSD ComplexType definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD ComplexType defintion
+# http://www.w3schools.com/Schema/el_simpletype.asp
+class ComplexType
+
+ # complex type attribute values
+ attr_accessor :id, :name, :abstract, :mixed
+
+ # complex type children
+ attr_accessor :attributes, :attribute_groups,
+ :simple_content, :complex_content,
+ :choice, :group, :sequence
+
+ # complexType parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "complexType"
+ end
+
+ # return xsd node info
+ def info
+ "complexType id: #{@id} name: #{@name}"
+ end
+
+ # returns array of all children
+ def children
+ (@attributes + @attribute_groups).push(@simple_content).push(@complex_content).push(@choice).push(@group).push(@sequence)
+ end
+
+ # node passed in should be a xml node representing the complex type
+ def self.from_xml(node)
+ complexType = ComplexType.new
+ complexType.parent = node.parent.related
+ node.related = complexType
+
+ # TODO complexType attributes: | block, final, anyAttributes
+
+ complexType.id = node.attrs['id']
+ complexType.name = node.attrs['name']
+ complexType.abstract = node.attrs.has_key?('abstract') ? node.attrs['abstract'].to_b : false
+
+ if node.children.find { |c| c.name == SimpleContent.tag_name }.nil?
+ complexType.mixed = node.attrs.has_key?('mixed') ? node.attrs['mixed'].to_b : false
+ else
+ complexType.mixed = false
+ end
+
+ # TODO complexType children: | all, anyAttribute,
+ complexType.attributes = node.children_objs Attribute
+ complexType.attribute_groups = node.children_objs AttributeGroup
+ complexType.simple_content = node.child_obj SimpleContent
+ complexType.complex_content = node.child_obj ComplexContent
+ complexType.group = node.child_obj Group
+ complexType.choice = node.child_obj Choice
+ complexType.sequence = node.child_obj Sequence
+
+ return complexType
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ end
+
+ # convert complex type to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ # dispatch to simple / complex content to get class builder
+ @class_builder = nil
+
+ if !@simple_content.nil?
+ @class_builder = @simple_content.to_class_builder
+ elsif !@complex_content.nil?
+ @class_builder = @complex_content.to_class_builder
+ else
+ @class_builder = ClassBuilder.new
+ end
+
+ @class_builder.klass_name = @name.camelize unless @name.nil?
+
+ @attributes.each { |att|
+ @class_builder.attribute_builders.push att.to_class_builder
+ }
+ @attribute_groups.each { |atg|
+ atg.to_class_builders.each { |atcb|
+ @class_builder.attribute_builders.push atcb
+ }
+ }
+
+ if !@group.nil?
+ @group.to_class_builders { |gcb|
+ @class_builder.attribute_builders.push gcb
+ }
+ end
+
+ if !@choice.nil?
+ @choice.to_class_builders { |ccb|
+ @class_builder.attribute_builders.push ccb
+ }
+ end
+
+ if !@sequence.nil?
+ @sequence.to_class_builders { |scb|
+ @class_builder.attribute_builders.push scb
+ }
+ end
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
136 lib/rxsd/xsd/element.rb
@@ -0,0 +1,136 @@
+# The XSD Element definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Element defintion
+# http://www.w3schools.com/Schema/el_element.asp
+class Element
+
+ # element attribute values
+ attr_accessor :id, :name, :type,
+ :nillable, :abstract, :ref,
+ :substitionGroup, :form, :maxOccurs,
+ :minOccurs, :default, :fixed
+
+ # simple type in element
+ attr_accessor :simple_type
+
+ # complex type in element
+ attr_accessor :complex_type
+
+ # element parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "element"
+ end
+
+ # return xsd node info
+ def info
+ "element id: #{@id} name: #{@name} type: #{@type.class} ref: #{ref.nil? ? "" : ref.name} "
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @simple_type unless @simple_type.nil?
+ c.push @complex_type unless @complex_type.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the element
+ def self.from_xml(node)
+ element = Element.new
+ element.parent = node.parent.related
+ node.related = element
+
+ # TODO element attrs: | block / final
+ # TODO element children: | key, keyref, unique
+
+ element.id = node.attrs["id"]
+ element.name = node.attrs["name"]
+ element.type = node.attrs["type"]
+ element.nillable = node.attrs.has_key?("nillable") ? node.attrs["nillable"].to_b : false
+ element.abstract = node.attrs.has_key?("abstract") ? node.attrs["abstract"].to_b : false
+
+ unless node.parent.name == Schema.tag_name
+ element.ref = node.attrs["ref"]
+ element.substitionGroup = node.attrs["substitionGroup"]
+ element.form = node.attrs.has_key?("form") ?
+ node.attrs["form"] : node.root.attrs["elementFormDefault"]
+
+ element.maxOccurs = node.attrs.has_key?("maxOccurs") ?
+ (node.attrs["maxOccurs"] == "unbounded" ? "unbounded" : node.attrs["maxOccurs"].to_i) : 1
+ element.minOccurs = node.attrs.has_key?("minOccurs") ?
+ (node.attrs["minOccurs"] == "unbounded" ? "unbounded" : node.attrs["minOccurs"].to_i) : 1
+
+ else
+ element.form = node.parent.attrs["elementFormDefault"]
+ end
+
+ if node.text? || !node.children.find { |c| c.name == SimpleType.tag_name }.nil?
+ element.default = node.attrs["default"]
+ element.fixed = node.attrs["fixed"]
+ end
+
+ element.simple_type = node.child_obj SimpleType
+ element.complex_type = node.child_obj ComplexType
+
+ return element
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @type.nil?
+ builtin = Parser.parse_builtin_type @type
+ @type = !builtin.nil? ? builtin : node_objs.find { |no| (no.class == SimpleType || no.class == ComplexType) &&
+ no.name == @type }
+ end
+
+ unless @ref.nil?
+ @ref = node_objs.find { |no| no.class == Element && no.name == @ref }
+ end
+
+ unless @substitionGroup.nil?
+ @substitutionGroup = node_objs.find { |no| no.class == Element && no.name == @substitionGroup }
+ end
+ end
+
+ # convert element to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ @class_builder = nil
+
+ if !@ref.nil?
+ @class_builder = @ref.to_class_builder
+
+ elsif !@type.nil?
+ if @type.class == SimpleType || @type.class == ComplexType
+ @class_builder = @type.to_class_builder
+ else
+ @class_builder = ClassBuilder.new :klass => @type
+ end
+
+ elsif !@simple_type.nil?
+ @class_builder = @simple_type.to_class_builder
+
+ elsif !@complex_type.nil?
+ @class_builder = @complex_type.to_class_builder
+
+ end
+
+ @class_builder.klass_name = @name unless @class_builder.nil?
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
119 lib/rxsd/xsd/extension.rb
@@ -0,0 +1,119 @@
+# The XSD Extension definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Extension defintion
+# http://www.w3schools.com/Schema/el_extension.asp
+class Extension
+
+ # extension attributes
+ attr_accessor :id, :base
+
+ # extension group children
+ attr_accessor :group, :choice, :sequence, :attributes, :attribute_groups
+
+ # extension parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "extension"
+ end
+
+ # return xsd node info
+ def info
+ "extension id: #{@id} base: #{@base.nil? ? "" : Parser.is_builtin?(@base) ? @base : @base.name }"
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @group unless @group.nil?
+ c.push @choice unless @choice.nil?
+ c.push @sequence unless @sequence.nil?
+ c += @attributes unless @attributes.nil?
+ c += @attribute_groups unless @attribute_groups.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the extension
+ def self.from_xml(node)
+ extension = Extension.new
+ extension.parent = node.parent.related
+ node.related = extension
+
+ # TODO extension attributes: | anyAttributes
+ extension.id = node.attrs["id"]
+ extension.base = node.attrs["base"]
+
+ # TODO extension children: | anyAttribute
+ extension.group = node.child_obj Group
+ extension.choice = node.child_obj Choice
+ extension.sequence = node.child_obj Sequence
+ extension.attributes = node.children_objs Attribute
+ extension.attribute_groups = node.children_objs AttributeGroup
+
+ return extension
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @base.nil?
+ builtin = Parser.parse_builtin_type @base
+ @base = !builtin.nil? ? builtin : node_objs.find { |no| (no.class == SimpleType || no.class == ComplexType) &&
+ no.name == @base }
+ end
+ end
+
+ # convert extension to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ # convert extension to builder
+ if Parser.is_builtin? @base
+ @class_builder= ClassBuilder.new :base => @base
+ elsif !@base.nil?
+ @class_builder= ClassBuilder.new :base_builder => @base.to_class_builder
+ else
+ @class_builder= ClassBuilder.new
+ end
+
+ unless @group.nil?
+ @group.to_class_builders.each { |gcb|
+ @class_builder.attribute_builders.push gcb
+ }
+ end
+
+ unless @choice.nil?
+ @choice.to_class_builders.each { |ccb|
+ @class_builder.attribute_builders.push ccb
+ }
+ end
+
+ unless @sequence.nil?
+ @sequence.to_class_builders.each { |scb|
+ @class_builder.attribute_builders.push scb
+ }
+ end
+
+ @attributes.each { |att|
+ @class_builder.attribute_builders.push att.to_class_builder
+ }
+
+ @attribute_groups.each { |atg|
+ atg.to_class_builders.each { |atcb|
+ @class_builder.attribute_builders.push atcb
+ }
+ }
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
92 lib/rxsd/xsd/group.rb
@@ -0,0 +1,92 @@
+# The XSD Group definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Group defintion
+# http://www.w3schools.com/Schema/el_group.asp
+class Group
+
+ # group attributes
+ attr_accessor :id, :name, :ref, :maxOccurs, :minOccurs
+
+ # group children
+ attr_accessor :choice, :sequence
+
+ # group parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "group"
+ end
+
+ # return xsd node info
+ def info
+ "group id: #{@id} name: #{@name} ref: #{ref.nil? ? "" : ref.name} "
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @choice unless @choice.nil?
+ c.push @sequence unless @sequence.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the group
+ def self.from_xml(node)
+ group = Group.new
+ group.parent = node.parent.related
+ node.related = group
+
+ # TODO group attributes: | anyAttributes
+ group.id = node.attrs["id"]
+ group.name = node.attrs["name"]
+ group.ref = node.attrs["ref"]
+
+ group.maxOccurs = node.attrs.has_key?("maxOccurs") ?
+ (node.attrs["maxOccurs"] == "unbounded" ? "unbounded" : node.attrs["maxOccurs"].to_i) : 1
+ group.minOccurs = node.attrs.has_key?("minOccurs") ?
+ (node.attrs["minOccurs"] == "unbounded" ? "unbounded" : node.attrs["minOccurs"].to_i) : 1
+
+
+ # TODO group children: | element(?)
+ group.choice = node.child_obj Choice
+ group.sequence = node.child_obj Sequence
+
+ return group
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @ref.nil?
+ @ref = node_objs.find { |no| no.class == Group && no.name == @ref }
+ end
+ end
+
+ # convert group to array of class builders
+ def to_class_builders
+ unless defined? @class_builder
+ # just dispatch to ref or child
+ @class_builder = []
+
+ if !@ref.nil?
+ @class_builder = @ref.to_class_builders
+ elsif !@choice.nil?
+ @class_builder = @choice.to_class_builders
+ elsif !@sequence.nil?
+ @class_builder = @sequence.to_class_builders
+ end
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
91 lib/rxsd/xsd/list.rb
@@ -0,0 +1,91 @@
+# The XSD List definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD List defintion
+# http://www.w3schools.com/Schema/el_list.asp
+class List
+
+ # list attributes
+ attr_accessor :id, :itemType
+
+ # list children
+ attr_accessor :simple_type
+
+ # list parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "list"
+ end
+
+ # return xsd node info
+ def info
+ "list id: #{@id}"
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @simple_type unless @simple_type.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the list
+ def self.from_xml(node)
+ list = List.new
+ list.parent = node.parent.related
+ node.related = list
+
+ # TODO list attributes: | anyAttributes
+ list.id = node.attrs["id"]
+
+
+ if node.children.find { |c| c.name == SimpleType.tag_name }.nil?
+ list.itemType = node.attrs["itemType"]
+ else
+ list.simple_type = node.child_obj SimpleType
+ end
+
+ return list
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @itemType.nil?
+ builtin = Parser.parse_builtin_type @itemType
+ @itemType = !builtin.nil? ? builtin : node_objs.find { |no| no.class == SimpleType && no.name == @itemType }
+ end
+ end
+
+ # convert list to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ # convert list to builder producing array of classes specified by item type or simple type
+ @class_builder = ClassBuilder.new :klass => Array
+
+ if !@itemType.nil?
+ if @itemType.class == SimpleType
+ @class_builder.associated_builder = @itemType.to_class_builder
+ else
+ @class_builder.associated_builder = ClassBuilder.new :klass => @itemType
+ end
+
+ elsif !@simple_type.nil?
+ @class_builder.associated_builder = @simple_type.to_class_builder
+
+ end
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
166 lib/rxsd/xsd/restriction.rb
@@ -0,0 +1,166 @@
+# The XSD Restriction definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Restriction defintion
+# http://www.w3schools.com/Schema/el_restriction.asp
+class Restriction
+
+ # restriction attributes
+ attr_accessor :id, :base
+
+ # restriction group children
+ attr_accessor :group, :choice, :sequence, :attributes, :attribute_groups, :simple_type
+
+ # restrictions
+ attr_accessor :min_exclusive, :min_inclusive, :max_exclusive, :max_inclusive,
+ :total_digits, :fraction_digits, :length, :min_length, :max_length,
+ :enumerations, :whitespace, :pattern
+
+ # restriction parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "restriction"
+ end
+
+ # return xsd node info
+ def info
+ "extension id: #{@id} base: #{@base.nil? ? "" : Parser.is_builtin?(@base) ? @base : @base.name }"
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @group unless @group.nil?
+ c.push @choice unless @choice.nil?
+ c.push @sequence unless @sequence.nil?
+ c += @attributes unless @attributes.nil?
+ c += @attribute_groups unless @attribute_groups.nil?
+ c.push @simple_type unless @simple_type.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the restriction
+ def self.from_xml(node)
+ restriction = Restriction.new
+ restriction.parent = node.parent.related
+ node.related = restriction
+
+ # TODO restriction attributes: | anyAttributes
+ restriction.id = node.attrs["id"]
+ restriction.base = node.attrs["base"]
+
+ if node.parent.name == ComplexContent.tag_name
+ # TODO restriction children: | anyAttribute
+ restriction.group = node.child_obj Group
+ restriction.choice = node.child_obj Choice
+ restriction.sequence = node.child_obj Sequence
+ restriction.attributes = node.children_objs Attribute
+ restriction.attribute_groups = node.children_objs AttributeGroup
+
+ elsif node.parent.name == SimpleContent.tag_name
+ # TODO restriction children: | anyAttribute
+ restriction.attributes = node.children_objs Attribute
+ restriction.attribute_groups = node.children_objs AttributeGroup
+ restriction.simple_type = node.child_obj SimpleType
+ parse_restrictions(restriction, node)
+
+ else # SimpleType
+ restriction.attributes = []
+ restriction.attribute_groups = []
+ restriction.simple_type = node.child_obj SimpleType
+ parse_restrictions(restriction, node)
+
+ end
+
+ return restriction
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ unless @base.nil?
+ builtin = Parser.parse_builtin_type @base
+ @base = !builtin.nil? ? builtin : node_objs.find { |no| (no.class == SimpleType || no.class == ComplexType) &&
+ no.name == @base }
+ end
+ end
+
+ # convert restriction to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ # convert restriction to builder
+ if Parser.is_builtin? @base
+ @class_builder= ClassBuilder.new :base => @base
+ elsif !@base.nil?
+ @class_builder= ClassBuilder.new :base_builder => @base.to_class_builder
+ else
+ @class_builder= ClassBuilder.new
+ end
+
+ unless @group.nil?
+ @group.to_class_builders.each { |gcb|
+ @class_builder.attribute_builders.push gcb
+ }
+ end
+
+ unless @choice.nil?
+ @choice.to_class_builders.each { |ccb|
+ @class_builder.attribute_builders.push ccb
+ }
+ end
+
+ unless @sequence.nil?
+ @sequence.to_class_builders.each { |scb|
+ @class_builder.attribute_builders.push scb
+ }
+ end
+
+ @attributes.each { |att|
+ @class_builder.attribute_builders.push att.to_class_builder
+ }
+
+ @attribute_groups.each { |atg|
+ atg.to_class_builders.each { |atcb|
+ @class_builder.attribute_builders.push atcb
+ }
+ }
+
+ unless @simple_type.nil?
+ @class_builder.attribute_builders.push @simple_type.to_class_builder
+ end
+
+ # FIXME add facets
+ end
+
+ return @class_builder
+ end
+
+
+ private
+
+ # internal helper method
+ def self.parse_restrictions(restriction, node)
+ restriction.min_exclusive = node.child_value("minExclusive").to_i
+ restriction.min_inclusive = node.child_value("minInclusive").to_i
+ restriction.max_exclusive = node.child_value("maxExclusive").to_i
+ restriction.max_inclusive = node.child_value("maxInclusive").to_i
+ restriction.total_digits = node.child_value("totalDigits").to_i
+ restriction.fraction_digits = node.child_value("fractionDigits").to_i
+ restriction.length = node.child_value("length").to_i
+ restriction.min_length = node.child_value("minLength").to_i
+ restriction.max_length = node.child_value("maxLength").to_i
+ restriction.enumerations = node.child_values "enumeration"
+ restriction.whitespace = node.child_value "whitespace"
+ restriction.pattern = node.child_value "pattern"
+ end
+
+end
+
+end # module XSD
+end # module RXSD
114 lib/rxsd/xsd/schema.rb
@@ -0,0 +1,114 @@
+# The XSD Schema definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# Schema defintion, top level XSD element
+# http://www.w3schools.com/Schema/el_schema.asp
+class Schema
+
+ # hash of prefix => namespace values
+ # " is the key of the default namespace (or use default_namespace)
+ attr_accessor :namespaces
+
+ # schema attribute values
+ attr_accessor :version, :targetNamespace, :elementFormDefault, :attributeFormDefault
+
+ # arrays of children in schema
+ attr_accessor :elements, :simple_types, :complex_types, :attributes, :attribute_groups, :groups
+
+ # parent, always nil but here for conformity
+ attr_accessor :parent
+
+ # return default namespace
+ def default_namespace
+ @namespaces[""]
+ end
+
+ # xml tag name
+ def self.tag_name
+ "schema"
+ end
+
+ # return xsd node info
+ def info
+ "schema"
+ end
+
+ # returns array of all schema children
+ def children
+ ([@elements] + [@simple_types] + [@complex_types] + [@attributes] + [@attribute] + [@attribute_groups] + [@groups]).flatten
+ end
+
+ # node passed in should be a xml root node representing the schema
+ def self.from_xml(node)
+ schema = Schema.new
+ schema.parent = nil
+ node.related= schema
+
+ # set namespaces
+ schema.namespaces = {}
+ node.namespaces.each{ |ns| schema.namespaces[ns.prefix] = ns.href }
+
+ # parse version out of attrs
+ schema.version = node.attrs["version"]
+
+ # parse target namespace out of attrs
+ schema.targetNamespace = node.attrs["targetNamespace"]
+
+ # parse elementFormDefault out of attrs
+ schema.elementFormDefault = node.attrs["elementFormDefault"]
+
+ # parse attributeFormDefault out of attrs
+ schema.attributeFormDefault = node.attrs["attributeFormDefault"]
+
+ # TODO schema attrs: | blockDefault / finalDefault / anyAttr
+ # TODO schema children: | import, notation, redefine / anyChild
+ # FIXME handle "xs:include" (use Loader?)
+
+ # parse elements
+ schema.elements = node.children_objs Element
+
+ # parse simple types
+ schema.simple_types = node.children_objs SimpleType
+
+ # parse complex types
+ schema.complex_types = node.children_objs ComplexType
+
+ # parse attributes
+ schema.attributes = node.children_objs Attribute
+
+ # parse attribute groups
+ schema.attribute_groups = node.children_objs AttributeGroup
+
+ # parse groups
+ schema.groups = node.children_objs Group
+
+ return schema
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ # right now does nothing
+ # (possible resolve include/import here)
+ end
+
+ # convert schema to array of class builders
+ def to_class_builders
+ unless defined? @class_builder
+ @class_builders = []
+ @elements.each { |e|
+ @class_builders.push e.to_class_builder
+ }
+ end
+
+ return @class_builders
+ end
+
+end
+
+end # module XSD
+end # module RXSD
95 lib/rxsd/xsd/sequence.rb
@@ -0,0 +1,95 @@
+# The XSD Sequence definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD Sequence defintion
+# http://www.w3schools.com/Schema/el_sequence.asp
+class Sequence
+
+ # sequence attributes
+ attr_accessor :id, :maxOccurs, :minOccurs
+
+ # sequence children
+ attr_accessor :elements, :groups, :choices, :sequences
+
+ # sequence parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "sequence"
+ end
+
+ # return xsd node info
+ def info
+ "sequence id: #{@id}"
+ end
+
+ # returns array of all children
+ def children
+ @elements + @groups + @choices + @sequences
+ end
+
+ # node passed in should be a xml node representing the group
+ def self.from_xml(node)
+ sequence = Sequence.new
+ sequence.parent = node.parent.related
+ node.related = sequence
+
+ # TODO sequence attributes: | anyAttributes
+ sequence.id = node.attrs["id"]
+
+ sequence.maxOccurs = node.attrs.has_key?("maxOccurs") ?
+ (node.attrs["maxOccurs"] == "unbounded" ? "unbounded" : node.attrs["maxOccurs"].to_i) : 1
+ sequence.minOccurs = node.attrs.has_key?("minOccurs") ?
+ (node.attrs["minOccurs"] == "unbounded" ? "unbounded" : node.attrs["minOccurs"].to_i) : 1
+
+ # TODO sequence children: | any
+ sequence.elements = node.children_objs Element
+ sequence.groups = node.children_objs Group
+ sequence.choices = node.children_objs Choice
+ sequence.sequences = node.children_objs Sequence
+
+ return sequence
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ end
+
+ # convert sequence to array of class builders
+ def to_class_builders
+ # FIXME enforce "all attributes must appear in set order"
+
+ unless defined? @class_builders
+ @class_builders = []
+ @elements.each { |e|
+ @class_builders.push e.to_class_builder
+ }
+ @groups.each { |g|
+ g.to_class_builders { |gcb|
+ @class_builders.push gcb
+ }
+ }
+ @choices.each { |c|
+ c.to_class_builders { |ccb|
+ @class_builders.push ccb
+ }
+ }
+ @sequences.each { |s|
+ s.to_class_builders { |scb|
+ @class_builders.push scb
+ }
+ }
+ end
+
+ return @class_builders
+ end
+end
+
+end # module XSD
+end # module RXSD
78 lib/rxsd/xsd/simple_content.rb
@@ -0,0 +1,78 @@
+# The XSD SimpleContent definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD SimpleContent defintion
+# http://www.w3schools.com/Schema/el_simpleContent.asp
+class SimpleContent
+
+ # simple content attributes
+ attr_accessor :id
+
+ # simple content children
+ attr_accessor :restriction, :extension
+
+ # simple content parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "simpleContent"
+ end
+
+ # return xsd node info
+ def info
+ "simple_content id: #{@id}"
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @restriction unless @restriction.nil?
+ c.push @extension unless @extension.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the group
+ def self.from_xml(node)
+ simple_content = SimpleContent.new
+ simple_content.parent = node.parent.related
+ node.related = simple_content
+
+ # TODO group attributes: | anyAttributes
+ simple_content.id = node.attrs["id"]
+
+ simple_content.restriction = node.child_obj Restriction
+ simple_content.extension = node.child_obj Extension
+
+ return simple_content
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ end
+
+ # convert simple content to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ # dispatch to child restriction/extension
+ @class_builder = nil
+
+ if !@restriction.nil?
+ @class_builder= @restriction.to_class_builder
+ elsif !@extension.nil?
+ @class_builder= @extension.to_class_builder
+ end
+ end
+
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
92 lib/rxsd/xsd/simple_type.rb
@@ -0,0 +1,92 @@
+# The XSD SimpleType definition
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+module RXSD
+module XSD
+
+# XSD SimpleType defintion
+# http://www.w3schools.com/Schema/el_simpletype.asp
+class SimpleType
+
+ # simple type attribute values
+ attr_accessor :id, :name
+
+ # children in schema (only one will be populated)
+ attr_accessor :list, :restriction
+
+ # simpleType parent
+ attr_accessor :parent
+
+ # xml tag name
+ def self.tag_name
+ "simpleType"
+ end
+
+ # return xsd node info
+ def info
+ "simple_type id: #{@id} name: #{@name}"
+ end
+
+ # returns array of all children
+ def children
+ c = []
+ c.push @list unless @list.nil?
+ c.push @restriction unless @restriction.nil?
+ return c
+ end
+
+ # node passed in should be a xml node representing the simple type
+ def self.from_xml(node)
+ simpleType = SimpleType.new
+ simpleType.parent = node.parent.related
+ node.related = simpleType
+
+ # TODO simpleType attributes: | anyAttr
+ simpleType.id = node.attrs["id"]
+ simpleType.name = node.attrs["name"]
+
+ # parse lists
+ simpleType.list = node.child_obj List
+
+ # parse restrictions
+ simpleType.restriction = node.child_obj Restriction
+
+ # TODO simpleType children: | unions
+ #simpleType.unions = node.child_obj Union
+
+ return simpleType
+ end
+
+ # resolve hanging references given complete xsd node object array
+ def resolve(node_objs)
+ end
+
+ # convert simple type to class builder
+ def to_class_builder
+ unless defined? @class_builder
+ @class_builder = nil
+
+ if !@list.nil?
+ # dispatch to child list
+ @class_builder = @list.to_class_builder
+
+ elsif !@restriction.nil?
+ # grab restriction class builder w/ base class and facets
+ @class_builder = @restriction.to_class_builder
+
+ #else
+ # @class_builder = ClassBuilder.new
+
+ end
+ end
+
+ @class_builder.klass_name = @name.camelize unless @name.nil?
+ return @class_builder
+ end
+
+end
+
+end # module XSD
+end # module RXSD
19 test/all_tests.rb
@@ -0,0 +1,19 @@
+# loads and runs all tests for the rxsd project
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+require 'test/unit'
+require 'mocha'
+
+require File.dirname(__FILE__) + '/../lib/rxsd'
+
+include RXSD
+include RXSD::XSD
+
+require 'test/loader_test'
+require 'test/parser_test'
+require 'test/resolver_test'
+require 'test/builder_test'
+require 'test/translator_test'
+#Dir['**/*_test.rb'].each { |test_case| require test_case }
92 test/builder_test.rb
@@ -0,0 +1,92 @@
+# tests the builder module
+#
+# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
+# See COPYING for the License of this software
+
+class BuilderTest < Test::Unit::TestCase
+ def setup
+ end
+
+ def teardown
+ end
+
+ # FIXME test to_class_builder method on all XSD classes!
+
+ def test_associated
+ gp = ClassBuilder.new
+ p = ClassBuilder.new :base_builder => gp
+ c = ClassBuilder.new :base_builder => p
+ as = ClassBuilder.new
+ c.associated_builder = as
+ at1 = ClassBuilder.new
+ at2 = ClassBuilder.new
+ c.attribute_builders.push at1
+ c.attribute_builders.push at2
+
+ ab = c.associated
+ assert_equal 5, ab.size
+ end
+
+ def test_build_class
+ cb1 = ClassBuilder.new :klass => String, :klass_name => "Widget"
+ assert_equal String, cb1.build_class
+
+ cb2 = ClassBuilder.new :klass_name => "Foobar"
+ c2 = cb2.build_class
+ assert_equal Foobar, c2
+ assert_equal Object, c2.superclass
+
+ acb = ClassBuilder.new :klass => Array, :klass_name => "ArrSocket", :associated_builder => cb1
+ ac = acb.build_class
+ assert_equal Array, ac
+
+ tcb = ClassBuilder.new :klass_name => "CamelCased"
+
+ cb3 = ClassBuilder.new :klass_name => "Foomoney", :base_builder => cb2
+ cb3.attribute_builders.push cb1
+ cb3.attribute_builders.push tcb
+ cb3.attribute_builders.push acb
+ c3 = cb3.build_class
+ assert_equal Foomoney, c3
+ assert_equal Foobar, c3.superclass
+ c3i = c3.new
+ assert ! c3i.method(:widget).nil?
+ assert_equal 0, c3i.method(:widget).arity
+ assert ! c3i.method(:widget=).nil?
+ assert_equal 1, c3i.method(:widget=).arity
+ assert ! c3i.method(:camel_cased).nil?
+ assert_equal 0, c3i.method(:camel_cased).arity
+ assert ! c3i.method(:camel_cased=).nil?
+ assert_equal 1, c3i.method(:camel_cased=).arity
+ assert ! c3i.method(:arr_socket).nil?
+ assert_equal 0, c3i.method(:arr_socket).arity
+ assert ! c3i.method(:arr_socket=).nil?
+ assert_equal 1, c3i.method(:arr_socket=).arity
+ end
+
+ def test_build_definition
+ cb1 = ClassBuilder.new :klass => String, :klass_name => "Widget"
+ assert_equal "class String\nend", cb1.build_definition
+
+ cb2 = ClassBuilder.new :klass_name => "Foobar"
+ d2 = cb2.build_definition
+ assert_equal "class Foobar < Object\nend", d2
+
+ acb = ClassBuilder.new :klass => Array, :klass_name => "ArrSocket", :associated_builder => cb1
+ ad = acb.build_definition
+ assert_equal "class Array\nend", ad
+
+ tcb = ClassBuilder.new :klass_name => "CamelCased"
+
+ cb3 = ClassBuilder.new :klass_name => "Foomoney", :base_builder => cb2
+ cb3.attribute_builders.push cb1
+ cb3.attribute_builders.push tcb
+ cb3.attribute_builders.push acb
+ d3 = cb3.build_definition
+ assert_equal "class Foomoney < Foobar\n" +
+ "attr_accessor :widget\n" +
+ "attr_accessor :camel_cased\n" +
+ "attr_accessor :arr_socket\n" +
+ "end", d3
+ end