rxsd 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,54 @@
1
+ # RXSD Ruby Definition builder
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ module RXSD
7
+
8
+ # Implements the RXSD::ClassBuilder interface to build string Ruby Class Definitions from xsd
9
+ class RubyDefinitionBuilder < ClassBuilder
10
+
11
+ # implementation of RXSD::ClassBuilder::build
12
+ def build
13
+ return "class #{@klass.to_s}\nend" if Parser.is_builtin? @klass
14
+
15
+ # need the class name to build class
16
+ return nil if @klass_name.nil?
17
+
18
+ Logger.debug "building definition for #{@klass}/#{@klass_name} from xsd"
19
+
20
+ # defined class w/ base
21
+ superclass = "Object"
22
+ unless @base_builder.nil?
23
+ if ! @base_builder.klass_name.nil?
24
+ superclass = @base_builder.klass_name
25
+ elsif ! @base_builder.klass.nil?
26
+ superclass = @base_builder.klass.to_s
27
+ end
28
+ end
29
+ res = "class " + @klass_name + " < " + superclass + "\n"
30
+
31
+ # define accessors for attributes
32
+ @attribute_builders.each { |atb|
33
+ unless atb.nil?
34
+ att_name = nil
35
+ if !atb.attribute_name.nil?
36
+ att_name = atb.attribute_name.underscore
37
+ elsif !atb.klass_name.nil?
38
+ att_name = atb.klass_name.underscore
39
+ else
40
+ att_name = atb.klass.to_s.underscore
41
+ end
42
+
43
+ res += "attr_accessor :#{att_name}\n"
44
+ end
45
+ }
46
+ res += "end"
47
+
48
+ Logger.debug "definition #{res} built, returning"
49
+ return res
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,59 @@
1
+ # RXSD Ruby Object builder
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ module RXSD
7
+
8
+ # Implements the RXSD::ObjectBuilder interface to build Ruby Objects from a xsd-conforming xml doc
9
+ class RubyObjectBuilder < ObjectBuilder
10
+
11
+ # implementation of RXSD::ObjectBuilder::build
12
+ def build(schema)
13
+ # return object if already built
14
+ return @obj unless @obj.nil?
15
+
16
+ Logger.debug "instantiating class #{@tag_name} from xsd"
17
+
18
+ # find class builder corresponding to tag_name to instantiate
19
+ tags = schema.tags
20
+ klass = tags[@tag_name].klass
21
+
22
+ # instantiate the target class
23
+ if @content.nil? # not a text based obj, construct normally
24
+ @obj = klass.new
25
+ elsif klass == Array # special case when instantiating arrays, need to specify item type
26
+ @obj = klass.from_s @content, tags[@tag_name].associated_builder.klass
27
+ else
28
+ @obj = klass.from_s @content
29
+ end
30
+
31
+ # go through each attribute, find corresponding class builder,
32
+ # instantiate, and assign to object
33
+ @attributes.each { |atn, atv|
34
+ if tags.has_key? @tag_name + ":" + atn # FIXME how do we want to handle attributes that are not in the schema (eg the else here)
35
+ aklass = tags[@tag_name + ":" + atn].klass
36
+ if aklass == Array # special case when instantiating arrays, need to specify item type
37
+ val = aklass.from_s atv, tags[@tag_name + ":" + atn].associated_builder.klass
38
+ else
39
+ val = aklass.from_s atv
40
+ end
41
+ @obj.send("#{atn.underscore}=".intern, val)
42
+ end
43
+ }
44
+
45
+ # instantiate each child using builder and assign to object
46
+ @children.each { |child|
47
+ cob = RubyObjectBuilder.new(:builder => child)
48
+ cobj = cob.build(schema)
49
+ @obj.send("#{cob.tag_name.underscore}=".intern, cobj)
50
+ }
51
+
52
+ Logger.debug "object type #{@tag_name} instantiated, returning"
53
+
54
+ return @obj
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,82 @@
1
+ # RXSD builtin types
2
+ #
3
+ # Here we add some functionality to some basic
4
+ # Ruby types and define some of our own.
5
+ #
6
+ # Each type must be able to be instantiated with
7
+ # no arguments as well as from a string string parameter
8
+ # (exception is made in the case of Array)
9
+ #
10
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
11
+ # See COPYING for the License of this software
12
+
13
+ require 'date'
14
+
15
+ # Array, String, Time can be instantiated as is
16
+
17
+ class Array
18
+ # arrays take addition parameter when instantiating from
19
+ # string, the item type which to instantiate array elements w/
20
+ def self.from_s(str, item_type)
21
+ arr = []
22
+ str.split.each { |i|
23
+ arr.push item_type.from_s(i)
24
+ }
25
+ return arr
26
+ end
27
+ end
28
+
29
+ class String
30
+ # convert string to boolean
31
+ def to_b
32
+ return true if self == true || self =~ /^true$/i
33
+ return false if self == false || self.nil? || self =~ /^false$/i
34
+ raise ArgumentError, "invalid value for Boolean: \"#{self}\""
35
+ end
36
+
37
+ def self.from_s(str)
38
+ str
39
+ end
40
+ end
41
+
42
+ class Time
43
+ def self.from_s(str)
44
+ return Time.parse(str)
45
+ end
46
+ end
47
+
48
+ # ruby doesn't define Char class, so we dispatch to string
49
+ class Char < String
50
+ end
51
+
52
+ # Since we can't create new instances of Integer, Float,
53
+ # etc subclasses, we use the delegate module
54
+ # http://codeidol.com/other/rubyckbk/Numbers/Simulating-a-Subclass-of-Fixnum/
55
+ require 'delegate'
56
+
57
+ class XSDInteger < DelegateClass(::Integer)
58
+ def self.from_s(str)
59
+ str.to_i
60
+ end
61
+ end
62
+
63
+ class XSDFloat < DelegateClass(::Float)
64
+ def self.from_s(str)
65
+ str.to_f
66
+ end
67
+ end
68
+
69
+ # ruby doesn't define Boolean class, so we define one ourselves
70
+ class Boolean
71
+ def self.from_s(str)
72
+ str.to_b
73
+ end
74
+
75
+ def initialize(val=false)
76
+ @val = val
77
+ end
78
+
79
+ def nil?
80
+ return !@val
81
+ end
82
+ end
@@ -0,0 +1,69 @@
1
+ # Things that don't fit elsewhere
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ # we make use of the activesupport inflector
7
+ require 'active_support'
8
+
9
+ # logger support
10
+ require 'logger'
11
+
12
+ class Logger
13
+ def format_message(severity, timestamp, progname, msg)
14
+ "#{severity} #{timestamp} (#{$$}) #{msg}\n"
15
+ end
16
+ end
17
+
18
+ module RXSD
19
+ # Logger helper class
20
+ class Logger
21
+ private
22
+ LOG_LEVEL = ::Logger::FATAL # FATAL ERROR WARN INFO DEBUG
23
+
24
+ def self._instantiate_logger
25
+ unless defined? @@logger
26
+ @@logger = ::Logger.new(STDOUT)
27
+ @@logger.level = LOG_LEVEL
28
+ end
29
+ end
30
+
31
+ public
32
+ def self.method_missing(method_id, *args)
33
+ _instantiate_logger
34
+ @@logger.send(method_id, args)
35
+ end
36
+ def self.logger
37
+ _instantiate_logger
38
+ @@logger
39
+ end
40
+ end
41
+ end
42
+
43
+ class Module
44
+ # add virtual method support
45
+ def virtual(*methods)
46
+ methods.each do |m|
47
+ define_method(m) {
48
+ raise VirtualMethodCalledError, m
49
+ }
50
+ end
51
+ end
52
+
53
+ # add helper method to define a class method on any class
54
+ def class_method(method_name, &block)
55
+ (class << self; self; end).instance_eval do
56
+ define_method method_name, block
57
+ end
58
+ end
59
+ end
60
+
61
+ # read entire file into string
62
+ def File.read_all(path)
63
+ File.open(path, 'rb') {|file| return file.read }
64
+ end
65
+
66
+ # write contents of file from string
67
+ def File.write(path, str)
68
+ File.open(path, 'wb') {|file| file.write str }
69
+ end
@@ -0,0 +1,25 @@
1
+ # RXSD exceptions
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'uri' # use uri to parse sources
7
+
8
+ # add virtual method support
9
+ class VirtualMethodCalledError < RuntimeError
10
+ attr :name
11
+ def initialize(name)
12
+ super("Virtual function '#{name}' called")
13
+ @name = name
14
+ end
15
+ end
16
+
17
+ module RXSD
18
+ module Exceptions
19
+
20
+ # thrown when specified resource uri is invalid
21
+ class InvalidResourceUri
22
+ end
23
+
24
+ end # module Exceptions
25
+ end # module RXSD
@@ -0,0 +1,77 @@
1
+
2
+ # libxml adapter
3
+ #
4
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
5
+ # See COPYING for the License of this software
6
+
7
+ require 'rubygems'
8
+ require 'libxml' # based on libxml
9
+
10
+ module RXSD
11
+ module XML
12
+
13
+ # class prototype needed :-(
14
+ class Node
15
+ end
16
+
17
+ # some additions to libxml xml node interface
18
+ class LibXMLNode < Node
19
+
20
+ # implementation of RXSD::XML::Node::xml_root(xml)
21
+ def self.xml_root(xml)
22
+ LibXMLNode.new :node => LibXML::XML::Document.string(xml).root
23
+ end
24
+
25
+ # create libxml node adapter w/ specified args, which may include
26
+ # * :node LibXML::Node to use to satify requests
27
+ def initialize(args = {})
28
+ @node = args[:node]
29
+ end
30
+
31
+
32
+ # implementation of RXSD::XML::Node.name
33
+ def name
34
+ @node.name
35
+ end
36
+
37
+ # implementation of RXSD::XML::Node.attrs
38
+ def attrs
39
+ @node.attributes.to_h
40
+ end
41
+
42
+ # implementation of RXSD::XML::Node.parent?
43
+ def parent?
44
+ @node.parent? && @node.parent.class != LibXML::XML::Document
45
+ end
46
+
47
+ # implementation of RXSD::XML::Node.parent
48
+ def parent
49
+ parent? ? LibXMLNode.new(:node => @node.parent) : nil
50
+ end
51
+
52
+ # implementation of RXSD::XML::Node.children
53
+ def children
54
+ @node.children.collect { |n|
55
+ LibXMLNode.new :node => n
56
+ }
57
+ end
58
+
59
+ # implementation of RXSD::XML::Node.text?
60
+ def text?
61
+ @node.text?
62
+ end
63
+
64
+ # implementation of RXSD::XML::Node.content
65
+ def content
66
+ @node.content
67
+ end
68
+
69
+ # implementation of RXSD::XML::Node.namespaces
70
+ def namespaces
71
+ @node.namespaces
72
+ end
73
+
74
+ end
75
+
76
+ end # module XML
77
+ end # module RXSD
@@ -0,0 +1,33 @@
1
+ # RXSD resource loader
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'uri' # use uri to parse sources
7
+ require 'net/http' # get http:// based resources
8
+
9
+ module RXSD
10
+
11
+ # loads resources from uris
12
+ class Loader
13
+
14
+ # loads and return text resource from specified source uri
15
+ def self.load(source_uri)
16
+ Logger.info "loading resource from uri #{source_uri}"
17
+ data = nil
18
+ uri = URI.parse(source_uri)
19
+ if uri.scheme == "file"
20
+ data = File.read_all uri.path
21
+ elsif uri.scheme == "http"
22
+ data = Net::HTTP.get_response(uri.host, uri.path).body
23
+ # elsif FIXME support other uri types
24
+ end
25
+
26
+ return data
27
+
28
+ rescue URI::InvalidURIError
29
+ raise Exceptions::InvalidResourceUri
30
+ end
31
+
32
+ end # class loader
33
+ end # module RXSD
@@ -0,0 +1,135 @@
1
+ # xml / xsd parsers
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ module RXSD
7
+
8
+ # provides class methods to parse xsd and xml data
9
+ class Parser
10
+ private
11
+ def initialize
12
+ end
13
+
14
+ public
15
+
16
+ # parse xsd specified by uri or in raw data form into RXSD::XSD::Schema instance
17
+ # args should be a hash w/ optional keys:
18
+ # * :uri location which to load resource from
19
+ # * :raw raw data which to parse
20
+ def self.parse_xsd(args)
21
+ data = Loader.load(args[:uri]) unless args[:uri].nil?
22
+ data = args[:raw] unless args[:raw].nil?
23
+ Logger.debug "parsing xsd"
24
+
25
+ # FIXME validate against xsd's own xsd
26
+ root_xml_node = XML::Node.factory :backend => :libxml, :xml => data
27
+ schema = XSD::Schema.from_xml root_xml_node
28
+
29
+ Logger.debug "parsed xsd, resolving relationships"
30
+ Resolver.resolve_nodes schema
31
+
32
+ Logger.debug "xsd parsing complete"
33
+ return schema
34
+ end
35
+
36
+ # parse xml specified by uri or in raw data form into RXSD::XSD::SchemaInstance instance
37
+ def self.parse_xml(args)
38
+ data = Loader.load(args[:uri]) unless args[:uri].nil?
39
+ data = args[:raw] unless args[:raw].nil?
40
+ Logger.debug "parsing xml"
41
+
42
+ root_xml_node = XML::Node.factory :backend => :libxml, :xml => data
43
+ schema_instance = SchemaInstance.new :builders => SchemaInstance.builders_from_xml(root_xml_node)
44
+
45
+ Logger.debug "xml parsing complete"
46
+ return schema_instance
47
+ end
48
+
49
+ # return true is specified class is builtin, else false
50
+ def self.is_builtin?(builtin_class)
51
+ [Array, String, Boolean, Char, Time, XSDFloat, XSDInteger].include? builtin_class
52
+ end
53
+
54
+ # return ruby class corresponding to builting type
55
+ def self.parse_builtin_type(builtin_type_name)
56
+ res = nil
57
+
58
+ case builtin_type_name
59
+ when "xs:string":
60
+ res = String
61
+ when "xs:boolean":
62
+ res = Boolean
63
+ when "xs:decimal":
64
+ res = XSDFloat
65
+ when "xs:float":
66
+ res = XSDFloat
67
+ when "xs:double":
68
+ res = XSDFloat
69
+ when "xs:duration":
70
+ when "xs:dateTime":
71
+ res = Time
72
+ when "xs:date":
73
+ res = Time
74
+ when "xs:gYearMonth":
75
+ res = Time
76
+ when "xs:gYear":
77
+ res = Time
78
+ when "xs:gMonthDay":
79
+ res = Time
80
+ when "xs:gDay":
81
+ res = Time
82
+ when "xs:gMonth":
83
+ res = Time
84
+ when "xs:hexBinary":
85
+ when "xs:base64Binary":
86
+ when "xs:anyURI":
87
+ when "xs:QName":
88
+ when "xs:NOTATION":
89
+ when "xs:normalizedString"
90
+ when "xs:token"
91
+ res = String # FIXME should be a string derived class, eliminating whitespace
92
+ when "xs:language"
93
+ when "xs:NMTOKEN"
94
+ when "xs:NMTOKENS"
95
+ when "xs:Name"
96
+ when "xs:NCName"
97
+ when "xs:ID"
98
+ when "xs:IDREF"
99
+ when "xs:IDREFS"
100
+ when "xs:ENTITY"
101
+ when "xs:ENTITIES"
102
+ when "xs:integer"
103
+ res = XSDInteger
104
+ when "xs:nonPositiveInteger"
105
+ res = XSDInteger
106
+ when "xs:negativeInteger"
107
+ res = XSDInteger
108
+ when "xs:long"
109
+ res = XSDInteger
110
+ when "xs:int"
111
+ res = XSDInteger
112
+ when "xs:short"
113
+ res = XSDInteger
114
+ when "xs:byte"
115
+ res = Char
116
+ when "xs:nonNegativeInteger"
117
+ res = XSDInteger
118
+ when "xs:unsignedLong"
119
+ res = XSDInteger
120
+ when "xs:unsignedInt"
121
+ res = XSDInteger
122
+ when "xs:unsignedShort"
123
+ res = XSDInteger
124
+ when "xs:unsignedByte"
125
+ res = Char
126
+ when "xs:positiveInteger"
127
+ res = XSDInteger
128
+ end
129
+
130
+ return res
131
+ end
132
+
133
+ end
134
+
135
+ end