rxsd 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +8 -0
- data/LICENSE +165 -0
- data/README +19 -0
- data/bin/rxsd-test.rb +41 -0
- data/lib/rxsd.rb +22 -0
- data/lib/rxsd/builder.rb +159 -0
- data/lib/rxsd/builders/ruby_class.rb +79 -0
- data/lib/rxsd/builders/ruby_definition.rb +54 -0
- data/lib/rxsd/builders/ruby_object.rb +59 -0
- data/lib/rxsd/builtin_types.rb +82 -0
- data/lib/rxsd/common.rb +69 -0
- data/lib/rxsd/exceptions.rb +25 -0
- data/lib/rxsd/libxml_adapter.rb +77 -0
- data/lib/rxsd/loader.rb +33 -0
- data/lib/rxsd/parser.rb +135 -0
- data/lib/rxsd/resolver.rb +52 -0
- data/lib/rxsd/translator.rb +127 -0
- data/lib/rxsd/xml.rb +92 -0
- data/lib/rxsd/xsd/attribute.rb +119 -0
- data/lib/rxsd/xsd/attribute_group.rb +90 -0
- data/lib/rxsd/xsd/choice.rb +109 -0
- data/lib/rxsd/xsd/complex_content.rb +87 -0
- data/lib/rxsd/xsd/complex_type.rb +136 -0
- data/lib/rxsd/xsd/element.rb +162 -0
- data/lib/rxsd/xsd/extension.rb +138 -0
- data/lib/rxsd/xsd/group.rb +100 -0
- data/lib/rxsd/xsd/list.rb +101 -0
- data/lib/rxsd/xsd/restriction.rb +186 -0
- data/lib/rxsd/xsd/schema.rb +114 -0
- data/lib/rxsd/xsd/sequence.rb +108 -0
- data/lib/rxsd/xsd/simple_content.rb +86 -0
- data/lib/rxsd/xsd/simple_type.rb +101 -0
- metadata +106 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# RXSD resolver
|
2
|
+
#
|
3
|
+
# resolves hanging node relationships and provides overall node access
|
4
|
+
#
|
5
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
6
|
+
# See COPYING for the License of this software
|
7
|
+
|
8
|
+
module RXSD
|
9
|
+
|
10
|
+
# resolves
|
11
|
+
class Resolver
|
12
|
+
|
13
|
+
# return hash of xsd types -> array of type instances for all nodes
|
14
|
+
# underneath given node_obj (inclusive)
|
15
|
+
def self.node_objects(node_obj, args = {})
|
16
|
+
if args.has_key? :node_objs
|
17
|
+
node_objs = args[:node_objs]
|
18
|
+
else
|
19
|
+
node_objs = {XSD::Attribute => [], XSD::AttributeGroup => [], XSD::Choice => [],
|
20
|
+
XSD::ComplexContent => [], XSD::ComplexType => [], XSD::Element => [],
|
21
|
+
XSD::Extension => [], XSD::Group => [], XSD::List => [], XSD::Restriction => [],
|
22
|
+
XSD::Schema => [], XSD::Sequence => [], XSD::SimpleContent => [], XSD::SimpleType => []}
|
23
|
+
|
24
|
+
unless node_obj.nil?
|
25
|
+
node_objs[node_obj.class].push node_obj
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
unless node_obj.nil?
|
30
|
+
node_obj.children.each{ |noc|
|
31
|
+
unless noc.nil? || node_objs[noc.class].include?(noc)
|
32
|
+
node_objs[noc.class].push noc
|
33
|
+
node_objs = node_objects(noc, :node_objs => node_objs) # might be better to do a breadth first traversal instead?
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
node_objs
|
39
|
+
end
|
40
|
+
|
41
|
+
# resolves hanging node relationships for specified schema
|
42
|
+
def self.resolve_nodes(schema)
|
43
|
+
node_objs = node_objects(schema)
|
44
|
+
node_objs.each { |xsd_class, nobjs|
|
45
|
+
nobjs.each{ |no|
|
46
|
+
no.resolve(node_objs)
|
47
|
+
}
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
end # class Resolver
|
52
|
+
end # module RXSD
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# RXSD translator
|
2
|
+
#
|
3
|
+
# transaltes xsd <-> ruby classes & xml <-> instances
|
4
|
+
#
|
5
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
6
|
+
# See COPYING for the License of this software
|
7
|
+
|
8
|
+
# include whatever output builders you want here,
|
9
|
+
require 'builders/ruby_class'
|
10
|
+
require 'builders/ruby_definition'
|
11
|
+
require 'builders/ruby_object'
|
12
|
+
|
13
|
+
module RXSD
|
14
|
+
|
15
|
+
# Extend XSD Schema Interface to
|
16
|
+
# translate xsd/xml to / from ruby classes/objects
|
17
|
+
module XSD
|
18
|
+
class Schema
|
19
|
+
|
20
|
+
# helper method, return hash of all tag names -> class builders under schema.
|
21
|
+
# An tag is an element or attribute name that can appear in a xml document
|
22
|
+
# conforming to the schema
|
23
|
+
def tags
|
24
|
+
unless defined? @tags
|
25
|
+
@tags = {}
|
26
|
+
Resolver.
|
27
|
+
node_objects(self)[Element].
|
28
|
+
find_all { |no| no.class == Element }.
|
29
|
+
each { |elem|
|
30
|
+
unless elem.name.nil?
|
31
|
+
@tags[elem.name] = elem.to_class_builder
|
32
|
+
eca = elem.child_attributes
|
33
|
+
eca.each { |att|
|
34
|
+
@tags[elem.name + ":" + att.name] = att.to_class_builder # prepend element to attribute name to prevent conflicts
|
35
|
+
} unless eca.nil?
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
return @tags
|
40
|
+
end
|
41
|
+
|
42
|
+
# helper method, return all class builders in/under schema
|
43
|
+
def all_class_builders
|
44
|
+
to_class_builders.collect { |cb| cb.associated.push cb }.flatten.uniq.compact # FIXME this only filters duplicates by obj id,
|
45
|
+
# its possible we have multiple objects refering
|
46
|
+
# to the same type, should filter these out here or sometime b4
|
47
|
+
end
|
48
|
+
|
49
|
+
# translates schema and all child entities to instances of specified output type.
|
50
|
+
# output_type may be one of
|
51
|
+
# * :ruby_classes
|
52
|
+
# * :ruby_definitions
|
53
|
+
def to(output_type)
|
54
|
+
cbs = all_class_builders
|
55
|
+
results = []
|
56
|
+
cbs.each { |cb|
|
57
|
+
# probably a better way to do this at some point than invoking the copy constructors
|
58
|
+
#
|
59
|
+
# FIXME we create class builders in the translator on the fly, this may cause
|
60
|
+
# problems for later operations that need to access class builder attributes which
|
61
|
+
# have been created
|
62
|
+
case(output_type)
|
63
|
+
when :ruby_classes
|
64
|
+
cl = RubyClassBuilder.new(:builder => cb).build
|
65
|
+
results.push cl unless results.include? cl
|
66
|
+
cb.klass = cl # small hack to get around the above fixme... for now
|
67
|
+
when :ruby_definitions
|
68
|
+
df = RubyDefinitionBuilder.new(:builder => cb).build
|
69
|
+
results.push df unless results.include? df
|
70
|
+
end
|
71
|
+
}
|
72
|
+
return results
|
73
|
+
end
|
74
|
+
|
75
|
+
end # class Schema
|
76
|
+
|
77
|
+
end # module XSD
|
78
|
+
|
79
|
+
# SchemaInstance contains an array of ObjectBuilders and provides
|
80
|
+
# mechanism to instantiate objects from conforming to a xsd schema
|
81
|
+
class SchemaInstance
|
82
|
+
|
83
|
+
# array of object builders represented by current instance
|
84
|
+
attr_accessor :object_builders
|
85
|
+
|
86
|
+
# return array of ObjectBuilders parsed out of a RXSD::XML::Node.
|
87
|
+
# Optionally specify parent ObjectBuilder to use
|
88
|
+
def self.builders_from_xml(node, parent = nil)
|
89
|
+
node_builder = ObjectBuilder.new(:tag_name => node.name, :attributes => node.attrs, :parent => parent)
|
90
|
+
parent.children.push node_builder unless parent.nil? || parent.children.include?(node_builder)
|
91
|
+
builders = [ node_builder ]
|
92
|
+
node.children.each { |c|
|
93
|
+
if c.text?
|
94
|
+
node_builder.content = c.content if node.children.size == 1 # FIXME if text/children-elements be mixed under a node this wont work
|
95
|
+
else
|
96
|
+
builders += SchemaInstance.builders_from_xml(c, node_builder )
|
97
|
+
end
|
98
|
+
}
|
99
|
+
return builders
|
100
|
+
end
|
101
|
+
|
102
|
+
# create new schema instance w/ specified args
|
103
|
+
def initialize(args = {})
|
104
|
+
@object_builders = args[:builders] if args.has_key? :builders
|
105
|
+
end
|
106
|
+
|
107
|
+
# translates SchemaInstance's objects into instances of the specified output type.
|
108
|
+
# :output_type may be one of
|
109
|
+
# * :ruby_objects
|
110
|
+
# Must specify :schema argument containing RXSD::XSD::Schema to use in object creation
|
111
|
+
def to(output_type, args = {})
|
112
|
+
schema = args[:schema]
|
113
|
+
results = []
|
114
|
+
@object_builders.each { |ob|
|
115
|
+
# probably a better way to do this at some point than invoking the copy constructors
|
116
|
+
case(output_type)
|
117
|
+
when :ruby_objects
|
118
|
+
ob = RubyObjectBuilder.new(:builder => ob).build(schema)
|
119
|
+
results.push ob
|
120
|
+
end
|
121
|
+
}
|
122
|
+
return results
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end # module RXSD
|
data/lib/rxsd/xml.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# xml parsing subsystem
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
|
8
|
+
# require libxml adapter
|
9
|
+
require 'libxml_adapter'
|
10
|
+
|
11
|
+
module RXSD
|
12
|
+
module XML
|
13
|
+
|
14
|
+
# RXSD XML node interface subclasses must conform to and helper methods
|
15
|
+
class Node
|
16
|
+
|
17
|
+
# should return name of node, eg <foo> => "foo"
|
18
|
+
virtual :name
|
19
|
+
|
20
|
+
# return hash of attribute name / values
|
21
|
+
virtual :attrs
|
22
|
+
|
23
|
+
# should return bool if node has a parent
|
24
|
+
virtual :parent?
|
25
|
+
|
26
|
+
# should return this nodes's parent, if any
|
27
|
+
virtual :parent
|
28
|
+
|
29
|
+
# should return children nodes
|
30
|
+
virtual :children
|
31
|
+
|
32
|
+
# should return bool if node only contains text
|
33
|
+
virtual :text?
|
34
|
+
|
35
|
+
# should return string contents of text node
|
36
|
+
virtual :content
|
37
|
+
|
38
|
+
# should return list of namespaces corresponding to node
|
39
|
+
virtual :namespaces
|
40
|
+
|
41
|
+
# should also define class method 'xml_root' that returns a node instance corresponding
|
42
|
+
# to the root xml node in provided xml data
|
43
|
+
|
44
|
+
############################################################################
|
45
|
+
|
46
|
+
# node factory, returns root node corresponding to specified backend and xml data
|
47
|
+
def self.factory(args = {})
|
48
|
+
backend = args[:backend]
|
49
|
+
xml = args[:xml]
|
50
|
+
|
51
|
+
# add additional backend drivers here if desired
|
52
|
+
return LibXMLNode.xml_root(xml) if backend == :libxml
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# returns root node
|
57
|
+
def root
|
58
|
+
parent? ? parent.root : self
|
59
|
+
end
|
60
|
+
|
61
|
+
# provides accessor interface to related obj, in our case related xsd obj
|
62
|
+
attr_accessor :related
|
63
|
+
|
64
|
+
# instantiate all children of provided class type
|
65
|
+
def children_objs(klass)
|
66
|
+
elements = []
|
67
|
+
children.find_all { |c| c.name == klass.tag_name }.each { |c|
|
68
|
+
elements.push(klass.from_xml(c)) }
|
69
|
+
return elements
|
70
|
+
end
|
71
|
+
|
72
|
+
# instantiate first child of provided class type
|
73
|
+
def child_obj(klass)
|
74
|
+
return children_objs(klass)[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
# return 'value' attribute of all children w/ specified tag
|
78
|
+
def child_values(tag_name)
|
79
|
+
values = []
|
80
|
+
children.find_all { |c| c.name == tag_name }.each { |c| values.push(c.attrs['value']) }
|
81
|
+
return values
|
82
|
+
end
|
83
|
+
|
84
|
+
# return 'value' attribute of first childw/ specified tag
|
85
|
+
def child_value(tag_name)
|
86
|
+
return child_values(tag_name)[0]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end # module XML
|
92
|
+
end # module RXSD
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# The XSD Attribute definition
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
module RXSD
|
7
|
+
module XSD
|
8
|
+
|
9
|
+
# XSD Attribute defintion
|
10
|
+
# http://www.w3schools.com/Schema/el_attribute.asp
|
11
|
+
class Attribute
|
12
|
+
|
13
|
+
# attribute attributes
|
14
|
+
attr_accessor :id, :name, :use, :form, :default, :fixed, :ref, :type
|
15
|
+
|
16
|
+
# attribute children
|
17
|
+
attr_accessor :simple_type
|
18
|
+
|
19
|
+
# attribute parent
|
20
|
+
attr_accessor :parent
|
21
|
+
|
22
|
+
# xml tag name
|
23
|
+
def self.tag_name
|
24
|
+
"attribute"
|
25
|
+
end
|
26
|
+
|
27
|
+
# return xsd node info
|
28
|
+
def info
|
29
|
+
"attribute id: #{@id} name: #{@name} type: #{@type.class} ref: #{ref.nil? ? "" : ref.name} "
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns array of all children
|
33
|
+
def children
|
34
|
+
c = []
|
35
|
+
c.push @simple_type unless @simple_type.nil?
|
36
|
+
return c
|
37
|
+
end
|
38
|
+
|
39
|
+
# node passed in should be a xml node representing the attribute
|
40
|
+
def self.from_xml(node)
|
41
|
+
attribute = Attribute.new
|
42
|
+
attribute.parent = node.parent.related
|
43
|
+
node.related = attribute
|
44
|
+
|
45
|
+
# TODO attribute attributes: | anyAttributes
|
46
|
+
attribute.id = node.attrs["id"]
|
47
|
+
attribute.name = node.attrs["name"]
|
48
|
+
attribute.use = node.attrs["use"]
|
49
|
+
|
50
|
+
attribute.form = node.attrs.has_key?("form") ?
|
51
|
+
node.attrs["form"] : node.parent.attrs["attributeFormDefault"]
|
52
|
+
|
53
|
+
attribute.default = node.attrs["default"]
|
54
|
+
attribute.fixed = node.attrs["fixed"]
|
55
|
+
|
56
|
+
# FIXME ignoring reference namepsace prefix (if any) for now
|
57
|
+
ref = node.attrs["ref"]
|
58
|
+
ref = ref.split(':')[1] if !(ref.nil? || ref.index(":").nil?)
|
59
|
+
attribute.ref = ref
|
60
|
+
|
61
|
+
if node.children.find { |c| c.name == SimpleType.tag_name }.nil?
|
62
|
+
attribute.type = node.attrs["type"]
|
63
|
+
else
|
64
|
+
attribute.simple_type = node.child_obj SimpleType
|
65
|
+
end
|
66
|
+
|
67
|
+
return attribute
|
68
|
+
end
|
69
|
+
|
70
|
+
# resolve hanging references given complete xsd node object array
|
71
|
+
def resolve(node_objs)
|
72
|
+
unless @type.nil?
|
73
|
+
builtin = Parser.parse_builtin_type @type
|
74
|
+
@type = !builtin.nil? ? builtin : node_objs[SimpleType].find { |no| no.name == @type }
|
75
|
+
end
|
76
|
+
|
77
|
+
unless @ref.nil?
|
78
|
+
@ref = node_objs[Attribute].find { |no| no.name == @ref }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# convert complex type to class builder
|
83
|
+
def to_class_builder
|
84
|
+
unless defined? @class_builder
|
85
|
+
@class_builder = nil
|
86
|
+
if !@ref.nil?
|
87
|
+
@class_builder = @ref.to_class_builder
|
88
|
+
|
89
|
+
elsif !@type.nil?
|
90
|
+
if @type.class == SimpleType
|
91
|
+
@class_builder = @type.to_class_builder.clone # need to clone here as we're refering to a type that may be used elsewhere
|
92
|
+
else
|
93
|
+
@class_builder = ClassBuilder.new :klass => @type
|
94
|
+
end
|
95
|
+
|
96
|
+
elsif !@simple_type.nil?
|
97
|
+
@class_builder = @simple_type.to_class_builder
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
unless @class_builder.nil? || @name == "" || @name.nil?
|
102
|
+
@class_builder.attribute_name = @name
|
103
|
+
@class_builder.klass_name = @name.camelize if @class_builder.klass.nil? && @class_builder.klass_name.nil?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
return @class_builder
|
108
|
+
end
|
109
|
+
|
110
|
+
# return this attribute (or ref if appropriate) in array
|
111
|
+
def child_attributes
|
112
|
+
return [@ref] unless @ref.nil?
|
113
|
+
return [self]
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end # module XSD
|
119
|
+
end # module RXSD
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# The XSD AttributeGroup definition
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
module RXSD
|
7
|
+
module XSD
|
8
|
+
|
9
|
+
# XSD AttributeGroup defintion
|
10
|
+
# http://www.w3schools.com/Schema/el_attributegroup.asp
|
11
|
+
class AttributeGroup
|
12
|
+
|
13
|
+
# attribute group attributes
|
14
|
+
attr_accessor :id, :name, :ref
|
15
|
+
|
16
|
+
# attribute group children
|
17
|
+
attr_accessor :attributes, :attribute_groups
|
18
|
+
|
19
|
+
# attribute group parent
|
20
|
+
attr_accessor :parent
|
21
|
+
|
22
|
+
# xml tag name
|
23
|
+
def self.tag_name
|
24
|
+
"attributeGroup"
|
25
|
+
end
|
26
|
+
|
27
|
+
# return xsd node info
|
28
|
+
def info
|
29
|
+
"attributeGroup id: #{@id} name: #{@name} ref: #{ref.nil? ? "" : ref.class == String ? ref : ref.name} "
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns array of all children
|
33
|
+
def children
|
34
|
+
@attributes + @attribute_groups
|
35
|
+
end
|
36
|
+
|
37
|
+
# node passed in should be a xml node representing the attribute group
|
38
|
+
def self.from_xml(node)
|
39
|
+
attribute_group = AttributeGroup.new
|
40
|
+
attribute_group.parent = node.parent.related
|
41
|
+
node.related = attribute_group
|
42
|
+
|
43
|
+
# TODO attribute group attributes: | anyAttributes
|
44
|
+
attribute_group.id = node.attrs["id"]
|
45
|
+
attribute_group.name = node.attrs["name"]
|
46
|
+
attribute_group.ref = node.attrs["ref"]
|
47
|
+
|
48
|
+
# TODO attribute group children: | anyAttribute
|
49
|
+
attribute_group.attributes = node.children_objs Attribute
|
50
|
+
attribute_group.attribute_groups = node.children_objs AttributeGroup
|
51
|
+
|
52
|
+
return attribute_group
|
53
|
+
end
|
54
|
+
|
55
|
+
# resolve hanging references given complete xsd node object array
|
56
|
+
def resolve(node_objs)
|
57
|
+
unless @ref.nil?
|
58
|
+
@ref = node_objs[AttributeGroup].find { |no| no.name == @ref }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# convert attribute group to array of class builders
|
63
|
+
def to_class_builders
|
64
|
+
unless defined? @class_builders
|
65
|
+
@class_builders = []
|
66
|
+
@attributes.each { |att|
|
67
|
+
@class_builders.push att.to_class_builder
|
68
|
+
}
|
69
|
+
@attribute_groups.each { |atg|
|
70
|
+
atg.to_class_builders.each { |atcb|
|
71
|
+
@class_builders.push atcb
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
return @class_builders
|
77
|
+
end
|
78
|
+
|
79
|
+
# return all child attributes associated w/ attribute group
|
80
|
+
def child_attributes
|
81
|
+
atts = []
|
82
|
+
@attribute_groups.each { |atg| atts += atg.child_attributes } unless @attribute_groups.nil?
|
83
|
+
@attributes.each { |att| atts += att.child_attributes } unless @attributes.nil?
|
84
|
+
return atts
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end # module XSD
|
90
|
+
end # module RXSD
|