bson 1.12.5-java → 2.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bson might be problematic. Click here for more details.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +80 -0
- data/CONTRIBUTING.md +42 -0
- data/NOTICE +2 -0
- data/README.md +190 -0
- data/Rakefile +109 -0
- data/lib/bson-ruby.jar +0 -0
- data/lib/bson.rb +60 -87
- data/lib/bson/array.rb +104 -0
- data/lib/bson/binary.rb +193 -0
- data/lib/bson/boolean.rb +48 -0
- data/lib/bson/code.rb +109 -0
- data/lib/bson/code_with_scope.rb +120 -0
- data/lib/bson/document.rb +549 -0
- data/lib/bson/encodable.rb +86 -0
- data/lib/bson/environment.rb +98 -0
- data/lib/bson/false_class.rb +61 -0
- data/lib/bson/float.rb +82 -0
- data/lib/bson/hash.rb +84 -0
- data/lib/bson/int32.rb +59 -0
- data/lib/bson/int64.rb +59 -0
- data/lib/bson/integer.rb +185 -0
- data/lib/bson/json.rb +37 -0
- data/lib/bson/max_key.rb +70 -0
- data/lib/bson/min_key.rb +70 -0
- data/lib/bson/nil_class.rb +70 -0
- data/lib/bson/object_id.rb +395 -0
- data/lib/bson/regexp.rb +124 -0
- data/lib/bson/registry.rb +70 -0
- data/lib/bson/specialized.rb +74 -0
- data/lib/bson/string.rb +203 -0
- data/lib/bson/symbol.rb +87 -0
- data/lib/bson/time.rb +72 -0
- data/lib/bson/timestamp.rb +113 -0
- data/lib/bson/true_class.rb +61 -0
- data/lib/bson/undefined.rb +74 -0
- data/lib/bson/version.rb +17 -0
- data/spec/bson/array_spec.rb +58 -0
- data/spec/bson/binary_spec.rb +115 -0
- data/spec/bson/boolean_spec.rb +48 -0
- data/spec/bson/code_spec.rb +42 -0
- data/spec/bson/code_with_scope_spec.rb +74 -0
- data/spec/bson/document_spec.rb +778 -0
- data/spec/bson/false_class_spec.rb +28 -0
- data/spec/bson/float_spec.rb +29 -0
- data/spec/bson/hash_spec.rb +56 -0
- data/spec/bson/int32_spec.rb +28 -0
- data/spec/bson/int64_spec.rb +28 -0
- data/spec/bson/integer_spec.rb +76 -0
- data/spec/bson/json_spec.rb +53 -0
- data/spec/bson/max_key_spec.rb +75 -0
- data/spec/bson/min_key_spec.rb +75 -0
- data/spec/bson/nil_class_spec.rb +29 -0
- data/spec/bson/object_id_spec.rb +527 -0
- data/spec/bson/regexp_spec.rb +89 -0
- data/spec/bson/registry_spec.rb +55 -0
- data/spec/bson/string_spec.rb +298 -0
- data/spec/bson/symbol_spec.rb +55 -0
- data/spec/bson/time_spec.rb +43 -0
- data/spec/bson/timestamp_spec.rb +74 -0
- data/spec/bson/true_class_spec.rb +28 -0
- data/spec/bson/undefined_spec.rb +29 -0
- data/{lib/bson/types/dbref.rb → spec/bson_spec.rb} +22 -16
- data/spec/spec_helper.rb +32 -0
- data/spec/support/shared_examples.rb +95 -0
- metadata +116 -48
- metadata.gz.sig +1 -1
- data/VERSION +0 -1
- data/bin/b2json +0 -63
- data/bin/j2bson +0 -64
- data/bson.gemspec +0 -34
- data/ext/jbson/lib/java-bson.jar +0 -0
- data/ext/jbson/target/jbson.jar +0 -0
- data/lib/bson/bson_c.rb +0 -37
- data/lib/bson/bson_java.rb +0 -49
- data/lib/bson/bson_ruby.rb +0 -645
- data/lib/bson/byte_buffer.rb +0 -241
- data/lib/bson/exceptions.rb +0 -37
- data/lib/bson/grow.rb +0 -173
- data/lib/bson/ordered_hash.rb +0 -197
- data/lib/bson/support/hash_with_indifferent_access.rb +0 -174
- data/lib/bson/types/binary.rb +0 -52
- data/lib/bson/types/code.rb +0 -55
- data/lib/bson/types/min_max_keys.rb +0 -56
- data/lib/bson/types/object_id.rb +0 -226
- data/lib/bson/types/regex.rb +0 -116
- data/lib/bson/types/timestamp.rb +0 -72
data/lib/bson/array.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module BSON
|
16
|
+
|
17
|
+
# Injects behaviour for encoding and decoding arrays to
|
18
|
+
# and from raw bytes as specified by the BSON spec.
|
19
|
+
#
|
20
|
+
# @see http://bsonspec.org/#/specification
|
21
|
+
#
|
22
|
+
# @since 2.0.0
|
23
|
+
module Array
|
24
|
+
include Encodable
|
25
|
+
|
26
|
+
# An array is type 0x04 in the BSON spec.
|
27
|
+
#
|
28
|
+
# @since 2.0.0
|
29
|
+
BSON_TYPE = 4.chr.force_encoding(BINARY).freeze
|
30
|
+
|
31
|
+
# Get the array as encoded BSON.
|
32
|
+
#
|
33
|
+
# @example Get the array as encoded BSON.
|
34
|
+
# [ 1, 2, 3 ].to_bson
|
35
|
+
#
|
36
|
+
# @note Arrays are encoded as documents, where the index of the value in
|
37
|
+
# the array is the actual key.
|
38
|
+
#
|
39
|
+
# @return [ String ] The encoded string.
|
40
|
+
#
|
41
|
+
# @see http://bsonspec.org/#/specification
|
42
|
+
#
|
43
|
+
# @since 2.0.0
|
44
|
+
def to_bson(encoded = ''.force_encoding(BINARY))
|
45
|
+
encode_with_placeholder_and_null(BSON_ADJUST, encoded) do |encoded|
|
46
|
+
each_with_index do |value, index|
|
47
|
+
encoded << value.bson_type
|
48
|
+
index.to_bson_key(encoded)
|
49
|
+
value.to_bson(encoded)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Convert the array to an object id. This will only work for arrays of size
|
55
|
+
# 12 where the elements are all strings.
|
56
|
+
#
|
57
|
+
# @example Convert the array to an object id.
|
58
|
+
# array.to_bson_object_id
|
59
|
+
#
|
60
|
+
# @note This is used for repairing legacy bson data.
|
61
|
+
#
|
62
|
+
# @raise [ InvalidObjectId ] If the array is not 12 elements.
|
63
|
+
#
|
64
|
+
# @return [ String ] The raw object id bytes.
|
65
|
+
#
|
66
|
+
# @since 2.0.0
|
67
|
+
def to_bson_object_id
|
68
|
+
ObjectId.repair(self) { pack("C*") }
|
69
|
+
end
|
70
|
+
|
71
|
+
module ClassMethods
|
72
|
+
|
73
|
+
# Deserialize the array from BSON.
|
74
|
+
#
|
75
|
+
# @param [ BSON ] bson The bson representing an array.
|
76
|
+
#
|
77
|
+
# @return [ Array ] The decoded array.
|
78
|
+
#
|
79
|
+
# @see http://bsonspec.org/#/specification
|
80
|
+
#
|
81
|
+
# @since 2.0.0
|
82
|
+
def from_bson(bson)
|
83
|
+
array = new
|
84
|
+
bson.read(4) # throw away the length
|
85
|
+
while (type = bson.readbyte.chr) != NULL_BYTE
|
86
|
+
bson.gets(NULL_BYTE)
|
87
|
+
array << BSON::Registry.get(type).from_bson(bson)
|
88
|
+
end
|
89
|
+
array
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Register this type when the module is loaded.
|
94
|
+
#
|
95
|
+
# @since 2.0.0
|
96
|
+
Registry.register(BSON_TYPE, ::Array)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Enrich the core Array class with this module.
|
100
|
+
#
|
101
|
+
# @since 2.0.0
|
102
|
+
::Array.send(:include, Array)
|
103
|
+
::Array.send(:extend, Array::ClassMethods)
|
104
|
+
end
|
data/lib/bson/binary.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module BSON
|
16
|
+
|
17
|
+
# Represents binary data.
|
18
|
+
#
|
19
|
+
# @see http://bsonspec.org/#/specification
|
20
|
+
#
|
21
|
+
# @since 2.0.0
|
22
|
+
class Binary
|
23
|
+
include JSON
|
24
|
+
include Encodable
|
25
|
+
|
26
|
+
# A binary is type 0x05 in the BSON spec.
|
27
|
+
#
|
28
|
+
# @since 2.0.0
|
29
|
+
BSON_TYPE = 5.chr.force_encoding(BINARY).freeze
|
30
|
+
|
31
|
+
# The mappings of subtypes to their single byte identifiers.
|
32
|
+
#
|
33
|
+
# @since 2.0.0
|
34
|
+
SUBTYPES = {
|
35
|
+
:generic => 0.chr,
|
36
|
+
:function => 1.chr,
|
37
|
+
:old => 2.chr,
|
38
|
+
:uuid_old => 3.chr,
|
39
|
+
:uuid => 4.chr,
|
40
|
+
:md5 => 5.chr,
|
41
|
+
:user => 128.chr
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
# The mappings of single byte subtypes to their symbol counterparts.
|
45
|
+
#
|
46
|
+
# @since 2.0.0
|
47
|
+
TYPES = SUBTYPES.invert.freeze
|
48
|
+
|
49
|
+
# @!attribute data
|
50
|
+
# @return [ Object ] The raw binary data.
|
51
|
+
# @since 2.0.0
|
52
|
+
# @!attribute type
|
53
|
+
# @return [ Symbol ] The binary type.
|
54
|
+
# @since 2.0.0
|
55
|
+
attr_reader :data, :type
|
56
|
+
|
57
|
+
# Determine if this binary object is equal to another object.
|
58
|
+
#
|
59
|
+
# @example Check the binary equality.
|
60
|
+
# binary == other
|
61
|
+
#
|
62
|
+
# @param [ Object ] other The object to compare against.
|
63
|
+
#
|
64
|
+
# @return [ true, false ] If the objects are equal.
|
65
|
+
#
|
66
|
+
# @since 2.0.0
|
67
|
+
def ==(other)
|
68
|
+
return false unless other.is_a?(Binary)
|
69
|
+
type == other.type && data == other.data
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the binary as JSON hash data.
|
73
|
+
#
|
74
|
+
# @example Get the binary as a JSON hash.
|
75
|
+
# binary.as_json
|
76
|
+
#
|
77
|
+
# @return [ Hash ] The binary as a JSON hash.
|
78
|
+
#
|
79
|
+
# @since 2.0.0
|
80
|
+
def as_json(*args)
|
81
|
+
{ "$binary" => data, "$type" => type }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Instantiate the new binary object.
|
85
|
+
#
|
86
|
+
# @example Instantiate a binary.
|
87
|
+
# BSON::Binary.new(data, :md5)
|
88
|
+
#
|
89
|
+
# @param [ Object ] data The raw binary data.
|
90
|
+
# @param [ Symbol ] type The binary type.
|
91
|
+
#
|
92
|
+
# @since 2.0.0
|
93
|
+
def initialize(data = "", type = :generic)
|
94
|
+
validate_type!(type)
|
95
|
+
@data = data
|
96
|
+
@type = type
|
97
|
+
end
|
98
|
+
|
99
|
+
# Encode the binary type
|
100
|
+
#
|
101
|
+
# @example Encode the binary.
|
102
|
+
# binary.to_bson
|
103
|
+
#
|
104
|
+
# @return [ String ] The encoded binary.
|
105
|
+
#
|
106
|
+
# @see http://bsonspec.org/#/specification
|
107
|
+
#
|
108
|
+
# @since 2.0.0
|
109
|
+
def to_bson(encoded = ''.force_encoding(BINARY))
|
110
|
+
encode_binary_data_with_placeholder(encoded) do |encoded|
|
111
|
+
encoded << SUBTYPES.fetch(type)
|
112
|
+
encoded << data.bytesize.to_bson if type == :old
|
113
|
+
encoded << data
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Deserialize the binary data from BSON.
|
118
|
+
#
|
119
|
+
# @param [ BSON ] bson The bson representing binary data.
|
120
|
+
#
|
121
|
+
# @return [ Binary ] The decoded binary data.
|
122
|
+
#
|
123
|
+
# @see http://bsonspec.org/#/specification
|
124
|
+
#
|
125
|
+
# @since 2.0.0
|
126
|
+
def self.from_bson(bson)
|
127
|
+
length = Int32.from_bson(bson)
|
128
|
+
type = TYPES[bson.read(1)]
|
129
|
+
length = Int32.from_bson(bson) if type == :old
|
130
|
+
data = bson.read(length)
|
131
|
+
new(data, type)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Raised when providing an invalid type to the Binary.
|
135
|
+
#
|
136
|
+
# @since 2.0.0
|
137
|
+
class InvalidType < RuntimeError
|
138
|
+
|
139
|
+
# @!attribute type
|
140
|
+
# @return [ Object ] The invalid type.
|
141
|
+
# @since 2.0.0
|
142
|
+
attr_reader :type
|
143
|
+
|
144
|
+
# Instantiate the new error.
|
145
|
+
#
|
146
|
+
# @example Instantiate the error.
|
147
|
+
# InvalidType.new(:error)
|
148
|
+
#
|
149
|
+
# @param [ Object ] type The invalid type.
|
150
|
+
#
|
151
|
+
# @since 2.0.0
|
152
|
+
def initialize(type)
|
153
|
+
@type = type
|
154
|
+
end
|
155
|
+
|
156
|
+
# Get the custom error message for the exception.
|
157
|
+
#
|
158
|
+
# @example Get the message.
|
159
|
+
# error.message
|
160
|
+
#
|
161
|
+
# @return [ String ] The error message.
|
162
|
+
#
|
163
|
+
# @since 2.0.0
|
164
|
+
def message
|
165
|
+
"#{type.inspect} is not a valid binary type. " +
|
166
|
+
"Please use one of #{SUBTYPES.keys.map(&:inspect).join(", ")}."
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
# Validate the provided type is a valid type.
|
173
|
+
#
|
174
|
+
# @api private
|
175
|
+
#
|
176
|
+
# @example Validate the type.
|
177
|
+
# binary.validate_type!(:user)
|
178
|
+
#
|
179
|
+
# @param [ Object ] type The provided type.
|
180
|
+
#
|
181
|
+
# @raise [ InvalidType ] The the type is invalid.
|
182
|
+
#
|
183
|
+
# @since 2.0.0
|
184
|
+
def validate_type!(type)
|
185
|
+
raise InvalidType.new(type) unless SUBTYPES.has_key?(type)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Register this type when the module is loaded.
|
189
|
+
#
|
190
|
+
# @since 2.0.0
|
191
|
+
Registry.register(BSON_TYPE, self)
|
192
|
+
end
|
193
|
+
end
|
data/lib/bson/boolean.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module BSON
|
16
|
+
|
17
|
+
# Represents a $maxKey type, which compares less than any other value in the
|
18
|
+
# specification.
|
19
|
+
#
|
20
|
+
# @see http://bsonspec.org/#/specification
|
21
|
+
#
|
22
|
+
# @since 2.0.0
|
23
|
+
class Boolean
|
24
|
+
|
25
|
+
# A boolean is type 0x08 in the BSON spec.
|
26
|
+
#
|
27
|
+
# @since 2.0.0
|
28
|
+
BSON_TYPE = 8.chr.force_encoding(BINARY).freeze
|
29
|
+
|
30
|
+
# Deserialize a boolean from BSON.
|
31
|
+
#
|
32
|
+
# @param [ BSON ] bson The encoded boolean.
|
33
|
+
#
|
34
|
+
# @return [ TrueClass, FalseClass ] The decoded boolean.
|
35
|
+
#
|
36
|
+
# @see http://bsonspec.org/#/specification
|
37
|
+
#
|
38
|
+
# @since 2.0.0
|
39
|
+
def self.from_bson(bson)
|
40
|
+
bson.readbyte == 1
|
41
|
+
end
|
42
|
+
|
43
|
+
# Register this type when the module is loaded.
|
44
|
+
#
|
45
|
+
# @since 2.0.0
|
46
|
+
Registry.register(BSON_TYPE, self)
|
47
|
+
end
|
48
|
+
end
|
data/lib/bson/code.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module BSON
|
16
|
+
|
17
|
+
# Represents a code type, which is a wrapper around javascript code.
|
18
|
+
#
|
19
|
+
# @see http://bsonspec.org/#/specification
|
20
|
+
#
|
21
|
+
# @since 2.0.0
|
22
|
+
class Code
|
23
|
+
include JSON
|
24
|
+
include Encodable
|
25
|
+
|
26
|
+
# A code is type 0x0D in the BSON spec.
|
27
|
+
#
|
28
|
+
# @since 2.0.0
|
29
|
+
BSON_TYPE = 13.chr.force_encoding(BINARY).freeze
|
30
|
+
|
31
|
+
# @!attribute javascript
|
32
|
+
# @return [ String ] The javascript code.
|
33
|
+
# @since 2.0.0
|
34
|
+
attr_reader :javascript
|
35
|
+
|
36
|
+
# Determine if this code object is equal to another object.
|
37
|
+
#
|
38
|
+
# @example Check the code equality.
|
39
|
+
# code == other
|
40
|
+
#
|
41
|
+
# @param [ Object ] other The object to compare against.
|
42
|
+
#
|
43
|
+
# @return [ true, false ] If the objects are equal.
|
44
|
+
#
|
45
|
+
# @since 2.0.0
|
46
|
+
def ==(other)
|
47
|
+
return false unless other.is_a?(Code)
|
48
|
+
javascript == other.javascript
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the code as JSON hash data.
|
52
|
+
#
|
53
|
+
# @example Get the code as a JSON hash.
|
54
|
+
# code.as_json
|
55
|
+
#
|
56
|
+
# @return [ Hash ] The code as a JSON hash.
|
57
|
+
#
|
58
|
+
# @since 2.0.0
|
59
|
+
def as_json(*args)
|
60
|
+
{ "$code" => javascript }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Instantiate the new code.
|
64
|
+
#
|
65
|
+
# @example Instantiate the new code.
|
66
|
+
# BSON::Code.new("this.value = 5")
|
67
|
+
#
|
68
|
+
# @param [ String ] javascript The javascript code.
|
69
|
+
#
|
70
|
+
# @since 2.0.0
|
71
|
+
def initialize(javascript = "")
|
72
|
+
@javascript = javascript
|
73
|
+
end
|
74
|
+
|
75
|
+
# Encode the javascript code.
|
76
|
+
#
|
77
|
+
# @example Encode the code.
|
78
|
+
# code.to_bson
|
79
|
+
#
|
80
|
+
# @return [ String ] The encoded string.
|
81
|
+
#
|
82
|
+
# @see http://bsonspec.org/#/specification
|
83
|
+
#
|
84
|
+
# @since 2.0.0
|
85
|
+
def to_bson(encoded = ''.force_encoding(BINARY))
|
86
|
+
encode_with_placeholder_and_null(STRING_ADJUST, encoded) do |encoded|
|
87
|
+
javascript.to_bson_string(encoded)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Deserialize code from BSON.
|
92
|
+
#
|
93
|
+
# @param [ BSON ] bson The encoded code.
|
94
|
+
#
|
95
|
+
# @return [ TrueClass, FalseClass ] The decoded code.
|
96
|
+
#
|
97
|
+
# @see http://bsonspec.org/#/specification
|
98
|
+
#
|
99
|
+
# @since 2.0.0
|
100
|
+
def self.from_bson(bson)
|
101
|
+
new(bson.read(Int32.from_bson(bson)).from_bson_string.chop!)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Register this type when the module is loaded.
|
105
|
+
#
|
106
|
+
# @since 2.0.0
|
107
|
+
Registry.register(BSON_TYPE, self)
|
108
|
+
end
|
109
|
+
end
|