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,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
|
data/lib/rxsd/common.rb
ADDED
@@ -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
|
data/lib/rxsd/loader.rb
ADDED
@@ -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
|
data/lib/rxsd/parser.rb
ADDED
@@ -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
|