udat 1.2.1 → 1.3.0
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.
- data/example.udat +242 -0
- data/lib/udat.rb +946 -840
- metadata +3 -2
data/example.udat
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
This is an example for a UDAT document.
|
2
|
+
|
3
|
+
[UDAT example|
|
4
|
+
|
5
|
+
|
6
|
+
Sample configuration file:
|
7
|
+
|
8
|
+
[sample config v1.0|
|
9
|
+
|
10
|
+
<network> [
|
11
|
+
<max connections> [256]
|
12
|
+
<reverse lookups> [yes]
|
13
|
+
]
|
14
|
+
|
15
|
+
<locale> [
|
16
|
+
<language> [de] german language
|
17
|
+
<timezone> [CET] and CET timezone
|
18
|
+
Comments can appear almost anywhere.
|
19
|
+
]
|
20
|
+
|
21
|
+
<access control> [
|
22
|
+
<allowed> [
|
23
|
+
[user|martin]
|
24
|
+
[user|max]
|
25
|
+
[group|admins]
|
26
|
+
[ip4network|
|
27
|
+
<address> [192.168.0.0]
|
28
|
+
<netmask> [255.255.255.0]
|
29
|
+
]
|
30
|
+
]
|
31
|
+
<blocked> [~] The tilde symbol denotes an empty collection.
|
32
|
+
]
|
33
|
+
|
34
|
+
<misc> [
|
35
|
+
<symbol for homedir> [\~] Not an empty collection but the scalar
|
36
|
+
value "~".
|
37
|
+
]
|
38
|
+
|
39
|
+
<address mappings> [
|
40
|
+
|
41
|
+
<email|
|
42
|
+
<user> [jan.behrens]
|
43
|
+
<domain> [flexiguided.de]
|
44
|
+
>
|
45
|
+
[mbox|jan]
|
46
|
+
|
47
|
+
<email|
|
48
|
+
<user> [glob|*]
|
49
|
+
<domain> [flexiguided.de]
|
50
|
+
>
|
51
|
+
[mbox|catchall]
|
52
|
+
|
53
|
+
]
|
54
|
+
|
55
|
+
<logging> [
|
56
|
+
<verbosity> [high]
|
57
|
+
\## <verbosity> [debug] ## Uncomment this for debug output.
|
58
|
+
<destination> [file|/var/log/sample.log]
|
59
|
+
]
|
60
|
+
|
61
|
+
]
|
62
|
+
|
63
|
+
|
64
|
+
General Information:
|
65
|
+
|
66
|
+
Every UDAT node can either be a scalar or a collection. Scalars have
|
67
|
+
content represented by a single string, while collections contain other
|
68
|
+
UDAT nodes.
|
69
|
+
|
70
|
+
Every UDAT node may be tagged, that means a meta-information in form of
|
71
|
+
a string is added. This information can be seen as type information.
|
72
|
+
|
73
|
+
Comments may appear anywhere in the document except in tags or scalars.
|
74
|
+
|
75
|
+
The following 7 characters are to be escaped with a backslash, unless
|
76
|
+
being used for their special meaning:
|
77
|
+
|
78
|
+
\< \> \[ \] \| \~ \\
|
79
|
+
|
80
|
+
|
81
|
+
Scalar values:
|
82
|
+
|
83
|
+
Any string without square or angle brackets or the tilde symbol is
|
84
|
+
considered to be a scalar:
|
85
|
+
|
86
|
+
[Hello World!]
|
87
|
+
|
88
|
+
You may add an additional tag, by preceding the string with the tag and
|
89
|
+
the pipe symbol:
|
90
|
+
|
91
|
+
[UTF-8|Hello World!]
|
92
|
+
|
93
|
+
Interpretation of tags is task of the application, there are no standard
|
94
|
+
tags defined.
|
95
|
+
|
96
|
+
Numbers are stored as strings too.
|
97
|
+
|
98
|
+
[23]
|
99
|
+
|
100
|
+
But they may be tagged for example with "integer", to clarify, that they
|
101
|
+
are integer numbers.
|
102
|
+
|
103
|
+
[integer|23]
|
104
|
+
|
105
|
+
You could store rational numbers like this:
|
106
|
+
|
107
|
+
[rational|2/3]
|
108
|
+
|
109
|
+
|
110
|
+
Collections:
|
111
|
+
|
112
|
+
Any string which includes either square or angle brackets or a tilde
|
113
|
+
symbol is considered to be a collection. Each entry in a collection
|
114
|
+
consists of a value and optionally a key associated with that value.
|
115
|
+
The key is written in angle brackets and being followed by the value in
|
116
|
+
square brackets. If the value has no key, the angle brackets are not
|
117
|
+
written.
|
118
|
+
|
119
|
+
Collection without keys: [ [1] [2] [3] ]
|
120
|
+
Collection with keys: [ <A>[1] <B>[2] <C>[3] ]
|
121
|
+
|
122
|
+
Collections may also contain both values with and without keys:
|
123
|
+
|
124
|
+
[ [0] <A>[1] <B>[2] <C>[3] ] ("0" has no key associated with it.)
|
125
|
+
|
126
|
+
Tags for collections are provided in the same way as for scalars:
|
127
|
+
|
128
|
+
[map| [0] <A>[1] <B>[2] <C>[3] ] (A collection tagged with "map")
|
129
|
+
|
130
|
+
Comments are allowed between the entries in brackets (as long as
|
131
|
+
characters with a special meaning are escaped):
|
132
|
+
|
133
|
+
[ comment [1] comment [2] comment [3] \[comment\] ]
|
134
|
+
|
135
|
+
Empty collections must include the tilde symbol, to avoid ambiguity with
|
136
|
+
scalars:
|
137
|
+
|
138
|
+
[ ] scalar containing three spaces
|
139
|
+
[ ~ ] empty collection
|
140
|
+
[list of numbers| ~ ] empty collection, tagged with "list of numbers"
|
141
|
+
[ ~ comment ] empty collection with a comment
|
142
|
+
[ comment ~ ] another empty collection with a comment
|
143
|
+
[~~] empty collection too (multiple ~ are allowed)
|
144
|
+
|
145
|
+
It is also allowed to write a tilde symbol in non-empty arrays. In that
|
146
|
+
case the tilde symbol is ignored.
|
147
|
+
|
148
|
+
[~ [1] [2] [3] ] Array containing "1", "2" and "3"
|
149
|
+
|
150
|
+
So the tilde symbol can be seen as a marker for collections, which is
|
151
|
+
mandatory in empty collections and optional in non-empty collections.
|
152
|
+
|
153
|
+
There is no standardized way to express, whether the order of a
|
154
|
+
collection is significant or not. It is task of the application to decide
|
155
|
+
that, so [[1][2][3]] could be seen as an ordered list of numbers or as a
|
156
|
+
set of numbers where the order is not significant. An application can use
|
157
|
+
tags to store the information, if neccessary, for example:
|
158
|
+
[set|[1][2][3]] vs. [array|[1][2][3]]
|
159
|
+
|
160
|
+
Examples of more complicated collections:
|
161
|
+
|
162
|
+
[group| <name>[Sample group] <members> [
|
163
|
+
[person| <firstname>[Max] <lastname>[Mustermann] ]
|
164
|
+
[person| <firstname>[Martin] <lastname>[html|Müller] ]
|
165
|
+
] ]
|
166
|
+
|
167
|
+
[ <&> [html|&]
|
168
|
+
<"> [html|"]
|
169
|
+
<\<> [html|<]
|
170
|
+
<\>> [html|>] ]
|
171
|
+
|
172
|
+
[ <color combination|[red][blue]> [nice]
|
173
|
+
<color combination|[red][magenta]> [ugly] ]
|
174
|
+
|
175
|
+
[ < <red>[on] <yellow>[off] <green>[off] > [stop]
|
176
|
+
< <red>[on] <yellow>[on] <green>[off] > [attention]
|
177
|
+
< <red>[off] <yellow>[off] <green>[on] > [go]
|
178
|
+
< <red>[off] <yellow>[on] <green>[off] > [prepare to stop] ]
|
179
|
+
|
180
|
+
|
181
|
+
Structured meta information:
|
182
|
+
|
183
|
+
Don't abuse tags to store complicated meta information like this:
|
184
|
+
|
185
|
+
\#WRONG# [string charset=ISO-8859-1 language=EN|Hello World!] #WRONG#
|
186
|
+
|
187
|
+
If you need to store more complicated meta information, you should do
|
188
|
+
that by using ordinary key/value pairs:
|
189
|
+
|
190
|
+
[string/w/metainfo v1.0|
|
191
|
+
<charset> [ISO-8859-1]
|
192
|
+
<language> [en]
|
193
|
+
<style> [folded]
|
194
|
+
[
|
195
|
+
Hello
|
196
|
+
World!
|
197
|
+
]
|
198
|
+
]
|
199
|
+
|
200
|
+
The tag "string/w/metainfo v1.0" is used only as the basic type
|
201
|
+
information (for the meta data and payload) here, the meta-data itself is
|
202
|
+
stored in key/value pairs, while the content is a value without key. An
|
203
|
+
application could interpret the "style" = "folded" information as a hint
|
204
|
+
to remove indentation and single line breaks, similar to folded scalars
|
205
|
+
in YAML, however this is application dependent and not part of the UDAT
|
206
|
+
format specification.
|
207
|
+
|
208
|
+
|
209
|
+
Binary safe parts and commenting-out:
|
210
|
+
|
211
|
+
There are two alternatives to disable interpretation of special
|
212
|
+
characters, without needing to add a backslash before each epecial
|
213
|
+
character:
|
214
|
+
|
215
|
+
Escape for a fixed length of bytes (useful to include binary blobs):
|
216
|
+
|
217
|
+
\$4$<>[]
|
218
|
+
|
219
|
+
Escape until boundary:
|
220
|
+
|
221
|
+
\## Here, no interpretation of <>[]|~\ is done. ##
|
222
|
+
|
223
|
+
\#boundary#
|
224
|
+
Same as above, but ## may be contained
|
225
|
+
and end is marked with: #boundary#
|
226
|
+
|
227
|
+
By using the \## ... ## syntax you can easily comment out parts of a
|
228
|
+
document:
|
229
|
+
|
230
|
+
[sample config v1.0|
|
231
|
+
<access control> [
|
232
|
+
<allowed> [ [user|martin] \##[user|max]## [group|admins] ]
|
233
|
+
]
|
234
|
+
]
|
235
|
+
|
236
|
+
But note that you have to specify a different boundary, if ## is
|
237
|
+
contained at any place (including scalar values) inside the commented-out
|
238
|
+
part.
|
239
|
+
|
240
|
+
|
241
|
+
]
|
242
|
+
|
data/lib/udat.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'monitor'
|
4
4
|
|
5
|
-
|
6
5
|
# Copyright (c) 2007 FlexiGuided GmbH, Berlin
|
7
6
|
#
|
8
7
|
# Author: Jan Behrens
|
@@ -11,1002 +10,1109 @@ require 'monitor'
|
|
11
10
|
#
|
12
11
|
# -----
|
13
12
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
13
|
+
# This module provides data structures to represent nodes of UDAT
|
14
|
+
# documents, a data format offering a generic basis for data storage and
|
15
|
+
# transmission, while being both easily readable by humans and machines.
|
16
|
+
# The format is comparable to formats like XML or YAML, but due to its
|
17
|
+
# simplicity is much more easy to parse.
|
19
18
|
#
|
20
|
-
# UDAT
|
21
|
-
#
|
19
|
+
# UDAT documents can be generated by converting any Object to a Udat::Node,
|
20
|
+
# by calling Object#to_udat, and then encoding them by calling
|
21
|
+
# Udat::Node#encode_document.
|
22
22
|
#
|
23
|
-
#
|
24
|
-
|
25
|
-
# be later parsed by String#parse_udat, or if it is enclosed in square
|
26
|
-
# brackets by IO#read_udat.
|
27
|
-
class Udat
|
23
|
+
# UDAT documents can be parsed by calling String#parse_udat_document.
|
24
|
+
module Udat
|
28
25
|
|
29
|
-
|
26
|
+
# Error raised by Udat::Collection#fetch,
|
27
|
+
# when a fetched value doesn't have the expected tag (i.e. type).
|
28
|
+
class UdatTagMismatch < StandardError; end
|
29
|
+
# Error raised by Udat::Collection#fetch,
|
30
|
+
# when a fetched value is a collection instead of a scalar or vice versa.
|
31
|
+
class UdatTypeMismatch < StandardError; end
|
32
|
+
# UDAT parsing error
|
33
|
+
class ParseError < StandardError; end
|
30
34
|
|
31
|
-
# Special argument passed to Udat#to_udat, if the tag is to be
|
35
|
+
# Special argument passed to Udat::Node#to_udat, if the tag is to be
|
36
|
+
# kept.
|
32
37
|
KeepTag = Object.new
|
33
38
|
|
34
39
|
# A string containing an UDAT example document.
|
35
40
|
ExampleDocument = <<-'END_OF_EXAMPLE'
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
[
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
\< \> \[ \] \| \~ \\
|
59
|
-
|
60
|
-
|
61
|
-
Arrays (or sets):
|
62
|
-
|
63
|
-
[ [1] [2] [3] ]
|
64
|
-
|
65
|
-
[ [1] [2] Comments are allowed here! (and alomost everywhere) [3] ]
|
66
|
-
|
67
|
-
[ [person| <firstname>[Max] <lastname>[Mustermann] ]
|
68
|
-
[person| <firstname>[Martin] <lastname>[html|Müller] ]
|
69
|
-
[group| <name>[Sample group] <members> [
|
70
|
-
[person| <firstname>[Max] <lastname>[Mustermann] ]
|
71
|
-
[person| <firstname>[Martin] <lastname>[html|Müller] ]
|
72
|
-
] ] ]
|
73
|
-
|
74
|
-
References like in YAML with &id and *id
|
75
|
-
are not directly supported by UDAT.
|
76
|
-
|
77
|
-
Empty arrays, sets or maps must contain the ~ character, to avoid
|
78
|
-
ambiguity with scalars:
|
79
|
-
|
80
|
-
[ ] scalar containing three spaces
|
81
|
-
[ ~ ] empty array
|
82
|
-
[list of numbers| ~ ] empty array, tagged with "list of numbers"
|
83
|
-
[ ~ comment ] empty array with a comment
|
84
|
-
[ comment ~ ] another empty array with a comment
|
85
|
-
[ ~~ ] empty array too (multiple ~ are allowed)
|
86
|
-
|
87
|
-
It is also allowed to write ~ in non-empty arrays. In that case the ~
|
88
|
-
symbol is ignored.
|
89
|
-
|
90
|
-
[~ [1] [2] [3] ] Array containing "1", "2" and "3"
|
91
|
-
|
92
|
-
|
93
|
-
Ordered or unordered maps:
|
94
|
-
|
95
|
-
[ <&> [html|&]
|
96
|
-
<"> [html|"]
|
97
|
-
<\<> [html|<]
|
98
|
-
<\>> [html|>] ]
|
99
|
-
|
100
|
-
[ <color combination|[red][blue]> [nice]
|
101
|
-
<color combination|[red][magenta]> [ugly] ]
|
102
|
-
|
103
|
-
[ < <red>[on] <yellow>[off] <green>[off] > [stop]
|
104
|
-
< <red>[on] <yellow>[on] <green>[off] > [attention]
|
105
|
-
< <red>[off] <yellow>[off] <green>[on] > [go]
|
106
|
-
< <red>[off] <yellow>[on] <green>[off] > [prepare to stop] ]
|
107
|
-
|
108
|
-
|
109
|
-
Sample configuration file:
|
110
|
-
|
111
|
-
[sample config v1.0|
|
112
|
-
<locale> [
|
113
|
-
<language> [de]
|
114
|
-
<timezone> [CET]
|
115
|
-
]
|
116
|
-
<logging> [
|
117
|
-
<verbosity> [high]
|
118
|
-
<destination> [file|/var/log/sample.log]
|
119
|
-
]
|
120
|
-
<network> [
|
121
|
-
<max connections> [256]
|
122
|
-
<reverse lookups> [yes]
|
123
|
-
]
|
124
|
-
<access control> [
|
125
|
-
<allowed> [ [user|martin] [user|max] [group|admins] ]
|
41
|
+
[sample config v1.0|
|
42
|
+
|
43
|
+
<network> [
|
44
|
+
<max connections> [256]
|
45
|
+
<reverse lookups> [yes]
|
46
|
+
]
|
47
|
+
|
48
|
+
<locale> [
|
49
|
+
<language> [de] german language
|
50
|
+
<timezone> [CET] and CET timezone
|
51
|
+
Comments can appear almost anywhere.
|
52
|
+
]
|
53
|
+
|
54
|
+
<access control> [
|
55
|
+
<allowed> [
|
56
|
+
[user|martin]
|
57
|
+
[user|max]
|
58
|
+
[group|admins]
|
59
|
+
[ip4network|
|
60
|
+
<address> [192.168.0.0]
|
61
|
+
<netmask> [255.255.255.0]
|
126
62
|
]
|
127
63
|
]
|
64
|
+
<blocked> [~] The tilde symbol denotes an empty collection.
|
65
|
+
]
|
128
66
|
|
67
|
+
<misc> [
|
68
|
+
<symbol for homedir> [\~] Not an empty collection but the scalar
|
69
|
+
value "~".
|
70
|
+
]
|
129
71
|
|
130
|
-
|
72
|
+
<address mappings> [
|
131
73
|
|
132
|
-
|
74
|
+
<email|
|
75
|
+
<user> [jan.behrens]
|
76
|
+
<domain> [flexiguided.de]
|
77
|
+
>
|
78
|
+
[mbox|jan]
|
133
79
|
|
80
|
+
<email|
|
81
|
+
<user> [glob|*]
|
82
|
+
<domain> [flexiguided.de]
|
83
|
+
>
|
84
|
+
[mbox|catchall]
|
134
85
|
|
135
|
-
|
136
|
-
that by using ordinary key/value pairs:
|
137
|
-
|
138
|
-
[string with metainfo v1.0|
|
139
|
-
<charset> [ISO-8859-1]
|
140
|
-
<language> [en]
|
141
|
-
<content> [Hello World!]
|
142
|
-
]
|
86
|
+
]
|
143
87
|
|
88
|
+
<logging> [
|
89
|
+
<verbosity> [high]
|
90
|
+
\## <verbosity> [debug] ## Uncomment this for debug output.
|
91
|
+
<destination> [file|/var/log/sample.log]
|
92
|
+
]
|
144
93
|
|
94
|
+
]
|
145
95
|
END_OF_EXAMPLE
|
146
96
|
|
147
|
-
#
|
148
|
-
#
|
149
|
-
class UdatTagMismatch < StandardError; end
|
150
|
-
# Error raised by UdatCollection#fetch,
|
151
|
-
# when a fetched value is a collection instead of a scalar or vice versa.
|
152
|
-
class UdatTypeMismatch < StandardError; end
|
153
|
-
# UDAT parsing error
|
154
|
-
class UdatParseError < StandardError; end
|
155
|
-
|
156
|
-
private_class_method :new
|
157
|
-
# Creates a new Udat object with a given tag (which may be nil).
|
158
|
-
# This method is private,
|
159
|
-
# Udat#initialize is only called through supercalls.
|
160
|
-
def initialize(tag)
|
161
|
-
super()
|
162
|
-
self.tag = tag
|
163
|
-
end
|
164
|
-
|
165
|
-
# Returns an escaped version of a string, where backslashes are preceding
|
166
|
-
# certain reserved characters.
|
167
|
-
def self.escape_string(string)
|
168
|
-
string.to_s.gsub /([<>\[\]|~\\])/, "\\\\\\1"
|
169
|
-
end
|
170
|
-
# Calls Udat.escape_string.
|
97
|
+
# Returns an escaped version of a string, where backslashes are
|
98
|
+
# preceding certain reserved characters.
|
171
99
|
def escape_string(string)
|
172
|
-
|
173
|
-
end
|
174
|
-
private :escape_string
|
175
|
-
|
176
|
-
# Tag (i.e. type of the content).
|
177
|
-
attr_reader :tag
|
178
|
-
# Sets the tag (i.e. type of the content).
|
179
|
-
def tag=(tag)
|
180
|
-
synchronize do
|
181
|
-
@tag = tag ? tag.to_s.dup.freeze : nil
|
182
|
-
end
|
183
|
-
return tag
|
184
|
-
end
|
185
|
-
|
186
|
-
# Returns true, if the UDAT object represents a scalar value.
|
187
|
-
def scalar?
|
188
|
-
kind_of? UdatScalar
|
189
|
-
end
|
190
|
-
# Returns true, if the UDAT object represents a collection.
|
191
|
-
def collection?
|
192
|
-
kind_of? UdatCollection
|
100
|
+
string.to_s.gsub /([<>\[\]|~\\])/, "\\\\\\1"
|
193
101
|
end
|
102
|
+
module_function :escape_string
|
194
103
|
|
195
|
-
#
|
104
|
+
# The abstract class to represent any UDAT data is a Udat::Node.
|
105
|
+
# Udat::Node's basically consist of a tag and the content. The tag can be
|
106
|
+
# a String or be nil. The content is either a scalar value stored as a
|
107
|
+
# String, or an ordered/unordered collection of values (where each value
|
108
|
+
# can optionally have a key associated with it). Keys and values of
|
109
|
+
# collections are always Udat::Node's too.
|
196
110
|
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
111
|
+
# Udat::Node's can most easily be constructed from other ruby objects by
|
112
|
+
# using the Object#to_udat method.
|
113
|
+
#
|
114
|
+
# By calling the method Udat::Node#encode_document, the Node and its
|
115
|
+
# children are encoded in a both easily human readable and easily machine
|
116
|
+
# readable format. The output can be later parsed by
|
117
|
+
# String#parse_udat_document or IO#read_udat.
|
118
|
+
class Node
|
119
|
+
|
120
|
+
include MonitorMixin
|
121
|
+
|
122
|
+
private_class_method :new
|
123
|
+
# Creates a new Udat::Node with a given tag (which may be nil). This
|
124
|
+
# method is private, Udat::Node#initialize is only called through
|
125
|
+
# supercalls.
|
126
|
+
def initialize(tag)
|
127
|
+
super()
|
128
|
+
self.tag = tag
|
208
129
|
end
|
209
|
-
end
|
210
|
-
# Here the method does the same as Udat#encode, but this method is
|
211
|
-
# overwritten in UdatScalar!
|
212
|
-
def to_s
|
213
|
-
encode
|
214
|
-
end
|
215
|
-
# Does the same as Udat#encode, but encloses the results in curly
|
216
|
-
# brackets and preceds them with the string "udat".
|
217
|
-
def inspect
|
218
|
-
"udat{#{self.encode}}"
|
219
|
-
end
|
220
130
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
return self
|
226
|
-
else
|
227
|
-
obj = nil
|
131
|
+
# Tag (i.e. type of the content).
|
132
|
+
attr_reader :tag
|
133
|
+
# Sets the tag (i.e. type of the content).
|
134
|
+
def tag=(tag)
|
228
135
|
synchronize do
|
229
|
-
|
136
|
+
@tag = tag ? tag.to_s.dup.freeze : nil
|
230
137
|
end
|
231
|
-
|
232
|
-
return obj
|
138
|
+
return tag
|
233
139
|
end
|
234
|
-
end
|
235
140
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
# Same as Udat#eql?, but behaves differently in UdatCollection.
|
248
|
-
def ==(other)
|
249
|
-
self.eql? other
|
250
|
-
end
|
141
|
+
# Returns true, if the Udat::Node represents a scalar value.
|
142
|
+
# Shortcut for Udat::Node#kind_of? Udat::Scalar.
|
143
|
+
def scalar?
|
144
|
+
kind_of? Scalar
|
145
|
+
end
|
146
|
+
# Returns true, if the Udat::Node represents a collection.
|
147
|
+
# Shortcut for Udat::Node#kind_of? Udat::Collection.
|
148
|
+
def collection?
|
149
|
+
kind_of? Collection
|
150
|
+
end
|
251
151
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
while pos < input.length
|
260
|
-
char = input[pos, 1]
|
261
|
-
case char
|
262
|
-
when "\\"
|
263
|
-
pos += 1
|
264
|
-
char = input[pos, 1]
|
265
|
-
if char.empty?
|
266
|
-
raise UdatParseError, "Backslash at end of input."
|
267
|
-
end
|
268
|
-
if char =~ /([<>\[\]|~\\])/
|
269
|
-
string << char if string
|
270
|
-
elsif char == "\n"
|
271
|
-
elsif char == "\r"
|
272
|
-
next_char = input[pos + 1, 1]
|
273
|
-
pos += 1 if next_char == "\n"
|
274
|
-
else
|
275
|
-
raise UdatParseError, "Unknown escape sequence found."
|
276
|
-
end
|
277
|
-
pos += 1
|
278
|
-
when "|"
|
152
|
+
# Returns the data (including it's tag) encoded in the UDAT format. The
|
153
|
+
# result is not enclosed by square brackets, so not intended to be
|
154
|
+
# used externally. This method is public for backwards compatiblity
|
155
|
+
# only, and will be declared protected in future.
|
156
|
+
# Use Udat::Node#encode_document instead.
|
157
|
+
def encode_part
|
158
|
+
synchronize do
|
279
159
|
if tag
|
280
|
-
|
281
|
-
end
|
282
|
-
unless string
|
283
|
-
raise UdatParseError, "Unexpected occurrence of pipe symbol."
|
284
|
-
end
|
285
|
-
tag = string
|
286
|
-
string = ""
|
287
|
-
pos += 1
|
288
|
-
when "~"
|
289
|
-
collection ||= UdatCollection.new(tag, [])
|
290
|
-
string = nil
|
291
|
-
pos += 1
|
292
|
-
when "<"
|
293
|
-
if key
|
294
|
-
raise UdatParseError, "Unexpected opening angle bracket."
|
295
|
-
end
|
296
|
-
key, pos = parse_intern(input, pos + 1)
|
297
|
-
unless input[pos, 1] == ">"
|
298
|
-
raise ArgumentError, "Closing angle bracket not found."
|
299
|
-
end
|
300
|
-
pos += 1
|
301
|
-
when ">"
|
302
|
-
break
|
303
|
-
when "["
|
304
|
-
value, pos = parse_intern(input, pos + 1)
|
305
|
-
unless input[pos, 1] == "]"
|
306
|
-
raise UdatParseError, "Closing square bracket not found."
|
307
|
-
end
|
308
|
-
collection ||= UdatCollection.new(tag, [])
|
309
|
-
if key
|
310
|
-
collection.append_pair(key, value)
|
311
|
-
key = nil
|
160
|
+
return "#{Udat::escape_string tag}|#{encoded_content}"
|
312
161
|
else
|
313
|
-
|
162
|
+
return encoded_content
|
314
163
|
end
|
315
|
-
|
316
|
-
|
317
|
-
|
164
|
+
end
|
165
|
+
end
|
166
|
+
# Returns the data (including it's tag) encoded in the UDAT format and
|
167
|
+
# enclosed in square brackets. This is the preferred form of encoding,
|
168
|
+
# when storing the data in files or over streams.
|
169
|
+
#
|
170
|
+
# Note: The UDAT format doesn't contain information, whether contained
|
171
|
+
# collections are ordered or unordered. This information is lost during
|
172
|
+
# the encoding process, and has to be restored in an application
|
173
|
+
# specific way, if neccessary.
|
174
|
+
def encode_document
|
175
|
+
"[#{encode_part}]"
|
176
|
+
end
|
177
|
+
# Deprecated. This method is an alias for Udat::Node#encode_part, but
|
178
|
+
# it is planned to be changed to Udat::Node#encode_document.
|
179
|
+
def encode
|
180
|
+
encode_part
|
181
|
+
end
|
182
|
+
# Here the method does the same as Udat::Node#encode_part, but this
|
183
|
+
# method is overwritten in Udat::Scalar! It is also planned to change
|
184
|
+
# the behaviour to Udat::Node#encode_document in future.
|
185
|
+
def to_s
|
186
|
+
encode_part
|
187
|
+
end
|
188
|
+
# Does the same as Udat::Node#encode_document, but preceds the result
|
189
|
+
# with "udat".
|
190
|
+
def inspect
|
191
|
+
"udat#{self.encode_document}"
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns self, or a duplicate of self with a different tag set, if an
|
195
|
+
# argument is passed.
|
196
|
+
def to_udat(tag = KeepTag)
|
197
|
+
if tag == KeepTag
|
198
|
+
return self
|
318
199
|
else
|
319
|
-
|
320
|
-
|
200
|
+
obj = nil
|
201
|
+
synchronize do
|
202
|
+
obj = self.dup
|
203
|
+
end
|
204
|
+
obj.tag = tag
|
205
|
+
return obj
|
321
206
|
end
|
322
207
|
end
|
323
|
-
raise UdatParseError, "Key without value." if key
|
324
|
-
return (collection ? collection : string.to_udat(tag)), pos
|
325
|
-
end
|
326
|
-
private_class_method :parse_intern
|
327
208
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
end
|
339
|
-
|
340
|
-
|
209
|
+
# Returns a hash key used by ruby's Hash'es.
|
210
|
+
def hash
|
211
|
+
to_s.hash
|
212
|
+
end
|
213
|
+
# Returns true, if class, tag and content (including it's order in case
|
214
|
+
# of a Udat::Collection) are matching another object.
|
215
|
+
def eql?(other)
|
216
|
+
self.class == other.class and
|
217
|
+
self.tag == other.tag and
|
218
|
+
self.to_s == other.to_s
|
219
|
+
end
|
220
|
+
# Same as Udat::Node#eql?, but behaves differently in Udat::Collection.
|
221
|
+
def ==(other)
|
222
|
+
self.eql? other
|
223
|
+
end
|
341
224
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
if char == "\\"
|
350
|
-
char = io.read(1)
|
351
|
-
return nil if char.nil? or char.length < 1
|
352
|
-
end
|
353
|
-
end while char != "[" and char != "<"
|
354
|
-
buffer = (char == "[") ? "" : nil
|
355
|
-
level = 1
|
225
|
+
# Internal parsing function.
|
226
|
+
def self.parse_intern(mode, end_char, &input_reader)
|
227
|
+
string = ""
|
228
|
+
tag = nil
|
229
|
+
collection = nil
|
230
|
+
key = nil
|
231
|
+
redo_char = false
|
356
232
|
while true
|
357
|
-
|
358
|
-
|
359
|
-
|
233
|
+
if redo_char
|
234
|
+
redo_char = false
|
235
|
+
else
|
236
|
+
char = input_reader.call
|
360
237
|
end
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
238
|
+
case char
|
239
|
+
when nil
|
240
|
+
if end_char.nil? and not key
|
241
|
+
break
|
242
|
+
else
|
243
|
+
raise EOFError, "Unexpected end of UDAT input."
|
244
|
+
end
|
245
|
+
when "\\"
|
246
|
+
char = input_reader.call
|
247
|
+
if char.nil?
|
248
|
+
raise EOFError, "Unexpected end of UDAT input."
|
249
|
+
end
|
250
|
+
if char =~ /([<>\[\]|~\\])/
|
251
|
+
string << char if string
|
252
|
+
elsif char == "\n"
|
253
|
+
elsif char == "\r"
|
254
|
+
char = input_reader.call
|
255
|
+
unless char == "\n"
|
256
|
+
redo_char = true
|
257
|
+
redo
|
258
|
+
end
|
259
|
+
elsif char == "#"
|
260
|
+
boundary = ""
|
261
|
+
while true
|
262
|
+
char = input_reader.call
|
263
|
+
if char.nil?
|
264
|
+
raise EOFError, "Unexpected end of UDAT input."
|
265
|
+
end
|
266
|
+
if char == "#"
|
267
|
+
break
|
268
|
+
else
|
269
|
+
boundary << char
|
270
|
+
end
|
271
|
+
end
|
272
|
+
while true
|
273
|
+
char = input_reader.call
|
274
|
+
if char.nil?
|
275
|
+
raise EOFError,
|
276
|
+
"Unexpected end of UDAT input " <<
|
277
|
+
"before ending boundary."
|
278
|
+
end
|
279
|
+
if char == "#"
|
280
|
+
boundary_end = ""
|
281
|
+
while true
|
282
|
+
char = input_reader.call
|
283
|
+
if char.nil?
|
284
|
+
raise EOFError,
|
285
|
+
"Unexpected end of UDAT input " <<
|
286
|
+
"before ending boundary."
|
287
|
+
end
|
288
|
+
if char == "#"
|
289
|
+
if boundary_end == boundary
|
290
|
+
break
|
291
|
+
else
|
292
|
+
string << "#" << boundary_end if string
|
293
|
+
boundary_end = ""
|
294
|
+
redo
|
295
|
+
end
|
296
|
+
else
|
297
|
+
boundary_end << char
|
298
|
+
end
|
299
|
+
end
|
300
|
+
break
|
301
|
+
else
|
302
|
+
string << char if string
|
303
|
+
end
|
304
|
+
end
|
305
|
+
elsif char == "$"
|
306
|
+
length = 0
|
307
|
+
while true
|
308
|
+
char = input_reader.call
|
309
|
+
if char.nil?
|
310
|
+
raise EOFError, "Unexpected end of UDAT input."
|
311
|
+
elsif char =~ /[0-9]/
|
312
|
+
length *= 10
|
313
|
+
length += char.to_i
|
314
|
+
elsif char == "$"
|
315
|
+
break
|
316
|
+
else
|
317
|
+
raise ParseError,
|
318
|
+
"Illegal character in length information of UDAT input."
|
319
|
+
end
|
320
|
+
end
|
321
|
+
length.times do
|
322
|
+
char = input_reader.call
|
323
|
+
if char.nil?
|
324
|
+
raise EOFError,
|
325
|
+
"Unexpected end of UDAT input in binary part."
|
326
|
+
end
|
327
|
+
string << char if string
|
328
|
+
end
|
329
|
+
else
|
330
|
+
raise ParseError, "Unknown escape sequence in UDAT input."
|
331
|
+
end
|
332
|
+
when "|"
|
333
|
+
if tag or not string
|
334
|
+
raise ParseError, "Unexpected pipe symbol in UDAT input."
|
335
|
+
end
|
336
|
+
tag = string
|
337
|
+
string = ""
|
338
|
+
when "~"
|
339
|
+
collection ||= Collection.new(tag, [])
|
340
|
+
string = nil
|
341
|
+
when "<"
|
342
|
+
if key
|
343
|
+
raise ParseError,
|
344
|
+
"Unexpected opening angle bracket in UDAT input."
|
345
|
+
end
|
346
|
+
key = parse_intern(:normal, ">", &input_reader)
|
347
|
+
string = nil
|
348
|
+
when ">"
|
349
|
+
if end_char == ">" and not key
|
350
|
+
break
|
351
|
+
else
|
352
|
+
raise ParseError,
|
353
|
+
"Unexpected closing angle bracket in UDAT input."
|
354
|
+
end
|
355
|
+
when "["
|
356
|
+
value = parse_intern(:normal, "]", &input_reader)
|
357
|
+
return value if mode == :one_value
|
358
|
+
collection ||= Collection.new(tag, [])
|
359
|
+
if key
|
360
|
+
collection.append_pair(key, value)
|
361
|
+
key = nil
|
362
|
+
else
|
363
|
+
collection.append_value(value)
|
364
|
+
end
|
365
|
+
string = nil
|
366
|
+
when "]"
|
367
|
+
if end_char == "]" and not key
|
368
|
+
break
|
369
|
+
else
|
370
|
+
raise ParseError,
|
371
|
+
"Unexpected closing square bracket in UDAT input."
|
366
372
|
end
|
367
|
-
elsif char == "[" or char == "<"
|
368
|
-
level += 1
|
369
|
-
elsif char == "]" or char == ">"
|
370
|
-
level -= 1
|
371
|
-
end
|
372
|
-
if level > 0
|
373
|
-
buffer << char if buffer
|
374
373
|
else
|
375
|
-
|
374
|
+
string << char if string
|
376
375
|
end
|
377
376
|
end
|
378
|
-
|
379
|
-
|
380
|
-
|
377
|
+
return nil if mode == :one_value
|
378
|
+
return collection ? collection : string.to_udat(tag)
|
379
|
+
end
|
380
|
+
private_class_method :parse_intern
|
381
|
+
|
382
|
+
# Deprecated. Parses a string encoded by Udat::Node#encode_part. This
|
383
|
+
# method might be removed in future versions. Use
|
384
|
+
# Udat::Node#encode_document in combination with
|
385
|
+
# Udat::Node.parse_document instead.
|
386
|
+
def self.parse_part(input)
|
387
|
+
input = input.to_s
|
388
|
+
pos = 0
|
389
|
+
begin
|
390
|
+
return (
|
391
|
+
parse_intern(:normal, nil) do
|
392
|
+
char = input[pos, 1]
|
393
|
+
pos += 1
|
394
|
+
next char.empty? ? nil : char
|
395
|
+
end
|
396
|
+
)
|
397
|
+
rescue EOFError
|
398
|
+
raise ParseError, $!.message
|
399
|
+
end
|
400
|
+
end
|
401
|
+
# Parses a given UDAT document string and returns a structure of
|
402
|
+
# Udat::Node's. It does the same as Udat::Node.parse_part(input).first.
|
403
|
+
#
|
404
|
+
# Note: When parsing UDAT data, no information is gained, whether
|
405
|
+
# collections are ordered or unordered. After parsing, all collections
|
406
|
+
# will be marked as unordered, unless changed later by the application.
|
407
|
+
def self.parse_document(input)
|
408
|
+
parse(input).first or raise ParseError, "No UDAT object found."
|
409
|
+
end
|
410
|
+
# Deprecated. This method is an alias for Udat::Node.parse_part, but
|
411
|
+
# in future it might be changed to Udat::Node.parse_document.
|
412
|
+
def self.parse(input)
|
413
|
+
parse_part(input)
|
414
|
+
end
|
381
415
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
416
|
+
# Reads Udat::Node encoded by Udat::Node#encode_document from a stream.
|
417
|
+
def self.read_from_stream(io)
|
418
|
+
return (
|
419
|
+
parse_intern(:one_value, nil) do
|
420
|
+
io.read(1)
|
421
|
+
end
|
422
|
+
)
|
423
|
+
end
|
388
424
|
|
389
|
-
|
425
|
+
# Encodes an object by calling Object#to_udat followed by
|
426
|
+
# Udat::Node#encode_document, and writes it to a stream. Returns self.
|
427
|
+
def write_to_stream(io)
|
428
|
+
io << self.to_udat.encode_document
|
429
|
+
return self
|
430
|
+
end
|
390
431
|
|
432
|
+
end
|
391
433
|
|
392
|
-
# Class of UDAT objects holding an ordered or unordered collection of
|
393
|
-
# values (where each value can optionally have a key associated with it).
|
394
|
-
# Keys and values of collections are always UDAT objects too.
|
395
|
-
# UdatCollection's can be interpreted as maps, arrays, sets or any other
|
396
|
-
# non-scalar data structure.
|
397
|
-
#
|
398
|
-
# Note: Keys and values are always implicitly casted by all instance
|
399
|
-
# methods of this class to Udat objects using Object#to_udat.
|
400
|
-
class UdatCollection < Udat
|
401
434
|
|
402
|
-
#
|
403
|
-
#
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
435
|
+
# Class of Udat::Node's holding an ordered or unordered collection of
|
436
|
+
# values (where each value can optionally have a key associated with it).
|
437
|
+
# Keys and values of collections are always Udat::Node's too.
|
438
|
+
# Udat::Collection's can be interpreted as maps, arrays, sets or any
|
439
|
+
# other non-scalar data structure.
|
440
|
+
#
|
441
|
+
# Note: Keys and values are always implicitly casted by all instance
|
442
|
+
# methods of this class to Udat::Node's using Object#to_udat.
|
443
|
+
class Collection < Node
|
444
|
+
|
445
|
+
# Internally used wrapper class for Udat::Node's, to lookup
|
446
|
+
# Udat::Collection's in Hash'es, while ignoring the order of their
|
447
|
+
# content.
|
448
|
+
class UnorderedWrapper
|
449
|
+
def initialize(content)
|
450
|
+
@content = content
|
451
|
+
end
|
452
|
+
attr_accessor :content
|
453
|
+
def hash
|
454
|
+
if content.kind_of? Collection
|
455
|
+
hash = 0
|
456
|
+
content.key_value_pairs.each do |key, value|
|
457
|
+
hash += key.hash
|
458
|
+
hash += value.hash
|
459
|
+
end
|
460
|
+
return hash
|
461
|
+
else
|
462
|
+
return content.hash
|
415
463
|
end
|
416
|
-
return hash
|
417
|
-
else
|
418
|
-
return content.hash
|
419
464
|
end
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
self.content.
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
465
|
+
def eql?(other)
|
466
|
+
return false unless self.class == other.class
|
467
|
+
unless self.content.kind_of? Node and
|
468
|
+
other.content.kind_of? Node
|
469
|
+
return self.content.eql?(other.content)
|
470
|
+
end
|
471
|
+
if self.content.class == other.content.class
|
472
|
+
self.content.tag == other.content.tag
|
473
|
+
if self.content.kind_of? Collection
|
474
|
+
own_pairs = self.content.key_value_pairs
|
475
|
+
other_pairs = other.content.key_value_pairs
|
476
|
+
own_pairs.each do |own_pair|
|
477
|
+
catch :found do
|
478
|
+
other_pairs.each_index do |index|
|
479
|
+
if own_pair.eql? other_pairs[index]
|
480
|
+
other_pairs.delete_at index
|
481
|
+
throw :found
|
482
|
+
end
|
434
483
|
end
|
484
|
+
return false
|
435
485
|
end
|
436
|
-
return false
|
437
486
|
end
|
487
|
+
return true
|
488
|
+
else
|
489
|
+
return self.content.eql?(other.content)
|
438
490
|
end
|
439
|
-
return true
|
440
491
|
else
|
441
|
-
return
|
492
|
+
return false
|
442
493
|
end
|
443
|
-
|
444
|
-
|
494
|
+
end
|
495
|
+
def ==(other)
|
496
|
+
self.eql? other
|
445
497
|
end
|
446
498
|
end
|
447
|
-
def ==(other)
|
448
|
-
self.eql? other
|
449
|
-
end
|
450
|
-
end
|
451
499
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
end
|
500
|
+
public_class_method :new
|
501
|
+
# Creates a new Udat::Collection with a given tag (which may be nil)
|
502
|
+
# and a given content, represented by a nested Array structure. It is
|
503
|
+
# not recommended to use this method. Use Object#to_udat instead.
|
504
|
+
def initialize(tag, content)
|
505
|
+
super tag
|
506
|
+
@ordered = true
|
507
|
+
@entries = []
|
508
|
+
require_index_hashes
|
509
|
+
content.to_ary.each { |entry| self << entry }
|
510
|
+
end
|
464
511
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
512
|
+
# Deletes the internal index hashes.
|
513
|
+
# They are automatically reconstructed once a lookup is done.
|
514
|
+
# This method should be called by the user, if contained Udat objects
|
515
|
+
# have changed, AFTER being added to this collection (similar to
|
516
|
+
# Hash#rehash).
|
517
|
+
# Returns self.
|
518
|
+
def rehash
|
519
|
+
synchronize do
|
520
|
+
@key_indicies = nil
|
521
|
+
@value_indicies = nil
|
522
|
+
@unordered_key_indicies = nil
|
523
|
+
@unordered_value_indicies = nil
|
524
|
+
@all_unordered_key_indicies = nil
|
525
|
+
@all_unordered_value_indicies = nil
|
526
|
+
end
|
527
|
+
return self
|
478
528
|
end
|
479
|
-
return self
|
480
|
-
end
|
481
529
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
530
|
+
# Returns true, if the internal index hashes are not existent.
|
531
|
+
def index_hashes_void?
|
532
|
+
synchronize do
|
533
|
+
return @key_indicies.nil?
|
534
|
+
end
|
486
535
|
end
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
@all_unordered_key_indicies[UdatUnorderedWrapper.new(key)] ||= []
|
504
|
-
@all_unordered_key_indicies[UdatUnorderedWrapper.new(key)] << index
|
505
|
-
@all_unordered_value_indicies[UdatUnorderedWrapper.new(value)] ||= []
|
506
|
-
@all_unordered_value_indicies[UdatUnorderedWrapper.new(value)] << index
|
507
|
-
return self
|
508
|
-
end
|
509
|
-
private :add_to_index_hashes
|
510
|
-
# Creates index hashes, if they are not existent.
|
511
|
-
def require_index_hashes
|
512
|
-
synchronize do
|
513
|
-
if index_hashes_void?
|
514
|
-
@key_indicies = {}
|
515
|
-
@value_indicies = {}
|
516
|
-
@unordered_key_indicies = {}
|
517
|
-
@unordered_value_indicies = {}
|
518
|
-
@all_unordered_key_indicies = {}
|
519
|
-
@all_unordered_value_indicies = {}
|
520
|
-
@entries.each_index do |index|
|
521
|
-
key_value_pair = @entries[index]
|
522
|
-
key = key_value_pair[0]
|
523
|
-
value = key_value_pair[1]
|
524
|
-
add_to_index_hashes(index, key, value) unless key.nil?
|
536
|
+
private :index_hashes_void?
|
537
|
+
# Registers a key value pair in the index.
|
538
|
+
# (The key argument may be nil for values without keys.)
|
539
|
+
def add_to_index_hashes(index, key, value)
|
540
|
+
unless index_hashes_void?
|
541
|
+
@key_indicies[key] ||= []
|
542
|
+
@key_indicies[key] << index
|
543
|
+
@value_indicies[value] ||= []
|
544
|
+
@value_indicies[value] << index
|
545
|
+
if key.kind_of? Collection and key.unordered?
|
546
|
+
@unordered_key_indicies[UnorderedWrapper.new(key)] ||= []
|
547
|
+
@unordered_key_indicies[UnorderedWrapper.new(key)] << index
|
548
|
+
end
|
549
|
+
if value.kind_of? Collection and value.unordered?
|
550
|
+
@unordered_value_indicies[UnorderedWrapper.new(value)] ||= []
|
551
|
+
@unordered_value_indicies[UnorderedWrapper.new(value)] << index
|
525
552
|
end
|
553
|
+
@all_unordered_key_indicies[UnorderedWrapper.new(key)] ||= []
|
554
|
+
@all_unordered_key_indicies[UnorderedWrapper.new(key)] << index
|
555
|
+
@all_unordered_value_indicies[UnorderedWrapper.new(value)] ||= []
|
556
|
+
@all_unordered_value_indicies[UnorderedWrapper.new(value)] << index
|
526
557
|
end
|
558
|
+
return self
|
527
559
|
end
|
528
|
-
|
529
|
-
|
530
|
-
|
560
|
+
private :add_to_index_hashes
|
561
|
+
# Creates index hashes, if they are not existent.
|
562
|
+
def require_index_hashes
|
563
|
+
synchronize do
|
564
|
+
if index_hashes_void?
|
565
|
+
@key_indicies = {}
|
566
|
+
@value_indicies = {}
|
567
|
+
@unordered_key_indicies = {}
|
568
|
+
@unordered_value_indicies = {}
|
569
|
+
@all_unordered_key_indicies = {}
|
570
|
+
@all_unordered_value_indicies = {}
|
571
|
+
@entries.each_index do |index|
|
572
|
+
key_value_pair = @entries[index]
|
573
|
+
key = key_value_pair[0]
|
574
|
+
value = key_value_pair[1]
|
575
|
+
add_to_index_hashes(index, key, value) unless key.nil?
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
return self
|
580
|
+
end
|
581
|
+
private :require_index_hashes
|
531
582
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
583
|
+
# Empties the collection. Returns self.
|
584
|
+
def clear
|
585
|
+
synchronize do
|
586
|
+
@entries.clear
|
587
|
+
rehash
|
588
|
+
require_index_hashes
|
589
|
+
end
|
590
|
+
return self
|
538
591
|
end
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
return key_value_pair ? key_value_pair[1] : nil
|
592
|
+
# Deletes the entry at the given numeric index.
|
593
|
+
# Returns the value, or nil if the index is out of bounds.
|
594
|
+
def delete_at(index)
|
595
|
+
index = index.to_int
|
596
|
+
synchronize do
|
597
|
+
key_value_pair = @entries.delete_at(index)
|
598
|
+
rehash
|
599
|
+
return key_value_pair ? key_value_pair[1] : nil
|
600
|
+
end
|
549
601
|
end
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
delete_at(0)
|
554
|
-
end
|
555
|
-
# Deletes the last entry and returns its value.
|
556
|
-
def pop
|
557
|
-
delete_at(-1)
|
558
|
-
end
|
559
|
-
# Deletes all entries with a given key.
|
560
|
-
def delete_key(key)
|
561
|
-
key = key.to_udat
|
562
|
-
synchronize do
|
563
|
-
@entries.delete_if { |e_key, e_value| e_key == key }
|
564
|
-
rehash
|
602
|
+
# Deletes the first entry and returns its value.
|
603
|
+
def shift
|
604
|
+
delete_at(0)
|
565
605
|
end
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
606
|
+
# Deletes the last entry and returns its value.
|
607
|
+
def pop
|
608
|
+
delete_at(-1)
|
609
|
+
end
|
610
|
+
# Deletes all entries with a given key. Returns self.
|
611
|
+
def delete_key(key)
|
612
|
+
key = key.to_udat
|
613
|
+
synchronize do
|
614
|
+
@entries.delete_if { |e_key, e_value| e_key == key }
|
615
|
+
rehash
|
616
|
+
end
|
617
|
+
return self
|
618
|
+
end
|
619
|
+
# Deletes all entries with a given value. Returns self.
|
620
|
+
def delete_value(value)
|
621
|
+
value = value.to_udat
|
622
|
+
synchronize do
|
623
|
+
@entries.delete_if { |e_key, e_value| e_value == value }
|
624
|
+
rehash
|
625
|
+
end
|
626
|
+
return self
|
574
627
|
end
|
575
|
-
return self
|
576
|
-
end
|
577
628
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
629
|
+
# Appends a new key with a new value. Returns self.
|
630
|
+
def append_pair(key, value)
|
631
|
+
key = key.to_udat
|
632
|
+
value = value.to_udat
|
633
|
+
synchronize do
|
634
|
+
index = @entries.length
|
635
|
+
@entries << [key, value]
|
636
|
+
add_to_index_hashes(index, key, value)
|
637
|
+
end
|
638
|
+
return self
|
586
639
|
end
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
640
|
+
# Appends a new value. Returns self.
|
641
|
+
def append_value(value)
|
642
|
+
value = value.to_udat
|
643
|
+
synchronize do
|
644
|
+
index = @entries.length
|
645
|
+
@entries << [nil, value]
|
646
|
+
add_to_index_hashes(index, nil, value)
|
647
|
+
end
|
648
|
+
return self
|
649
|
+
end
|
650
|
+
# Deletes all key/value pairs where the given key matches,
|
651
|
+
# and appends a new key/value pair. Returns self.
|
652
|
+
def set_value_for_key(key, value)
|
653
|
+
delete_key(key)
|
654
|
+
append_pair(key, value)
|
655
|
+
return self
|
656
|
+
end
|
657
|
+
# Deletes all values or key/value pairs where the given value matches,
|
658
|
+
# and appends a new key/value pair. Returns self.
|
659
|
+
def set_key_for_value(key, value)
|
660
|
+
delete_value(value)
|
661
|
+
append_pair(key, value)
|
662
|
+
return self
|
594
663
|
end
|
595
|
-
return self
|
596
|
-
end
|
597
|
-
# Deletes all key/value pairs where the given key matches,
|
598
|
-
# and appends a new key/value pair.
|
599
|
-
def set_value_for_key(key, value)
|
600
|
-
delete_key(key)
|
601
|
-
append_pair(key, value)
|
602
|
-
return self
|
603
|
-
end
|
604
|
-
# Deletes all values or key/value pairs where the given value matches,
|
605
|
-
# and appends a new key/value pair.
|
606
|
-
def set_key_for_value(key, value)
|
607
|
-
delete_value(value)
|
608
|
-
append_pair(key, value)
|
609
|
-
return self
|
610
|
-
end
|
611
664
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
665
|
+
# Behaves differently depending on the type of the argument. If the
|
666
|
+
# argument is an Array with 2 elements, a new key/value pair is added,
|
667
|
+
# with the key being the first element of the array, and the value
|
668
|
+
# being the second argument of the array. Arrays with any other number
|
669
|
+
# of elements cause an error. If the argument is not an array, then it
|
670
|
+
# is added as a value without a key.
|
671
|
+
def <<(value)
|
672
|
+
if value.kind_of? Array
|
673
|
+
unless value.length == 2
|
674
|
+
raise ArgumentError,
|
675
|
+
"#{value.length} array elements found where 2 were expected."
|
676
|
+
end
|
677
|
+
return append_pair(value[0], value[1])
|
678
|
+
else
|
679
|
+
return append_value(value)
|
680
|
+
end
|
681
|
+
end
|
682
|
+
# Same as Udat::Collection#set_value_for_key.
|
683
|
+
def []=(key, value)
|
684
|
+
set_value_for_key(key, value)
|
627
685
|
end
|
628
|
-
end
|
629
|
-
# Same as UdatCollection#set_value_for_key.
|
630
|
-
def []=(key, value)
|
631
|
-
set_value_for_key(key, value)
|
632
|
-
end
|
633
686
|
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
687
|
+
# Returns the value at a numeric index, or nil, if the index is out of
|
688
|
+
# bounds.
|
689
|
+
def value_by_index(index)
|
690
|
+
index = index.to_int
|
691
|
+
synchronize do
|
692
|
+
entry = @entries[index]
|
693
|
+
return entry ? entry[1] : nil
|
694
|
+
end
|
641
695
|
end
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
696
|
+
# Returns the key at a numeric index, or nil, if the index is out of
|
697
|
+
# bounds or at the given index there is only a value without key.
|
698
|
+
def key_by_index(index)
|
699
|
+
index = index.to_int
|
700
|
+
synchronize do
|
701
|
+
entry = @entries[index]
|
702
|
+
return entry ? entry[0] : nil
|
703
|
+
end
|
650
704
|
end
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
if key.kind_of? UdatCollection and key.unordered?
|
658
|
-
indicies = @all_unordered_key_indicies[
|
659
|
-
UdatUnorderedWrapper.new(key)
|
660
|
-
] || []
|
661
|
-
else
|
662
|
-
indicies = (@key_indicies[key] || []) + (
|
663
|
-
@unordered_key_indicies[UdatUnorderedWrapper.new(key)] || []
|
705
|
+
# Returns an Array of values having no key.
|
706
|
+
def values_without_key
|
707
|
+
synchronize do
|
708
|
+
require_index_hashes
|
709
|
+
return (
|
710
|
+
(@key_indicies[nil] || []).collect { |index| @entries[index][1] }
|
664
711
|
)
|
665
|
-
indicies.uniq!
|
666
|
-
indicies.sort!
|
667
712
|
end
|
668
|
-
return indicies.collect { |index| @entries[index][1] }
|
669
713
|
end
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
714
|
+
# Returns an Array of values having a given key.
|
715
|
+
def values_by_key(key)
|
716
|
+
key = key.to_udat
|
717
|
+
synchronize do
|
718
|
+
require_index_hashes
|
719
|
+
if key.kind_of? Collection and key.unordered?
|
720
|
+
indicies = @all_unordered_key_indicies[
|
721
|
+
UnorderedWrapper.new(key)
|
722
|
+
] || []
|
723
|
+
else
|
724
|
+
indicies = (@key_indicies[key] || []) + (
|
725
|
+
@unordered_key_indicies[UnorderedWrapper.new(key)] || []
|
726
|
+
)
|
727
|
+
indicies.uniq!
|
728
|
+
indicies.sort!
|
729
|
+
end
|
730
|
+
return indicies.collect { |index| @entries[index][1] }
|
731
|
+
end
|
732
|
+
end
|
733
|
+
# Returns an Array of keys having a given value. The Array may contain
|
734
|
+
# nil's for values having no key.
|
735
|
+
def keys_by_value(value)
|
736
|
+
value = value.to_udat
|
737
|
+
synchronize do
|
738
|
+
require_index_hashes
|
739
|
+
if value.kind_of? Collection and value.unordered?
|
740
|
+
indicies = @all_unordered_value_indicies[
|
741
|
+
UnorderedWrapper.new(value)
|
742
|
+
] || []
|
743
|
+
else
|
744
|
+
indicies = (@value_indicies[value] || []) + (
|
745
|
+
@unordered_value_indicies[UnorderedWrapper.new(value)] || []
|
746
|
+
)
|
747
|
+
indicies.uniq!
|
748
|
+
indicies.sort!
|
749
|
+
end
|
750
|
+
return indicies.collect { |index| @entries[index][0] }
|
751
|
+
end
|
752
|
+
end
|
753
|
+
# Returns the last value without key.
|
754
|
+
def value_without_key
|
755
|
+
values_without_key.last
|
756
|
+
end
|
757
|
+
# Returns the last value having a given key.
|
758
|
+
def value_by_key(key)
|
759
|
+
values_by_key(key).last
|
760
|
+
end
|
761
|
+
# Returns the last key having a given value.
|
762
|
+
def key_by_value(value)
|
763
|
+
keys_by_value(value).last
|
764
|
+
end
|
765
|
+
# Returns true, if the value is contained in the collection.
|
766
|
+
def include?(value)
|
767
|
+
value = value.to_udat
|
768
|
+
synchronize do
|
769
|
+
require_index_hashes
|
770
|
+
if value.kind_of? Collection and value.unordered?
|
771
|
+
return (@all_unordered_value_indicies[
|
772
|
+
UnorderedWrapper.new(value)
|
773
|
+
] || []).empty? ? false : true
|
774
|
+
else
|
775
|
+
return ((@value_indicies[value] || []) + (
|
776
|
+
@unordered_value_indicies[UnorderedWrapper.new(value)] || []
|
777
|
+
)).empty? ? false : true
|
778
|
+
end
|
686
779
|
end
|
687
|
-
return indicies.collect { |index| @entries[index][0] }
|
688
780
|
end
|
689
|
-
end
|
690
|
-
# Returns the last value having a given key.
|
691
|
-
def value_by_key(key)
|
692
|
-
values_by_key(key).last
|
693
|
-
end
|
694
|
-
# Returns the last key having a given value.
|
695
|
-
def key_by_value(value)
|
696
|
-
keys_by_value(value).last
|
697
|
-
end
|
698
781
|
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
782
|
+
# Behaves differently depending on the type of the argument.
|
783
|
+
# If the argument is an Integer, the method behaves same as
|
784
|
+
# Udat::Collection#value_by_index. If the argument is nil, the method
|
785
|
+
# behaves like Udat::Collection#value_without_key. Otherwise the method
|
786
|
+
# behaves same as Udat::Collection#value_by_key.
|
787
|
+
def [](key)
|
788
|
+
if key.nil?
|
789
|
+
return value_without_key
|
790
|
+
elsif key.kind_of? Integer
|
791
|
+
return value_by_index(key)
|
792
|
+
else
|
793
|
+
return value_by_key(key)
|
794
|
+
end
|
708
795
|
end
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
796
|
+
# Same as Udat::Collection#[], but raises an error, if no value was
|
797
|
+
# found. If an additional second argument is passed, an error will also
|
798
|
+
# be raised if the tag (i.e. type) of the value is not equal to the
|
799
|
+
# second argument.
|
800
|
+
def fetch(*args)
|
801
|
+
if args.length == 1
|
802
|
+
value = self[args[0]]
|
803
|
+
unless value
|
804
|
+
raise IndexError, "Value for the given key or index not found."
|
805
|
+
end
|
806
|
+
return value
|
807
|
+
elsif args.length == 2
|
808
|
+
value = fetch(args[0])
|
809
|
+
unless value.tag == args[1]
|
810
|
+
raise UdatTagMismatch, "UDAT tag mismatch."
|
811
|
+
end
|
812
|
+
return value
|
813
|
+
else
|
814
|
+
raise ArgumentError, "Wrong number of arguments supplied."
|
815
|
+
end
|
816
|
+
end
|
817
|
+
# Same as Udat::Collection#fetch, but raises an error, if the value is
|
818
|
+
# not an Udat::Collection.
|
819
|
+
def fetch_collection(*args)
|
820
|
+
value = fetch(*args)
|
821
|
+
if value.scalar?
|
822
|
+
raise UdatTypeMismatch,
|
823
|
+
"Scalar value found, where a collection was expected."
|
719
824
|
end
|
720
825
|
return value
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
826
|
+
end
|
827
|
+
# Same as Udat::Collection#fetch, but raises an error, if the value is
|
828
|
+
# not an Udat::Scalar.
|
829
|
+
def fetch_scalar(*args)
|
830
|
+
value = fetch(*args)
|
831
|
+
if value.collection?
|
832
|
+
raise UdatTypeMismatch,
|
833
|
+
"Collection found, where a scalar value was expected."
|
725
834
|
end
|
726
835
|
return value
|
727
|
-
else
|
728
|
-
raise ArgumentError, "Wrong number of arguments supplied."
|
729
836
|
end
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
def fetch_collection(*args)
|
734
|
-
value = fetch(*args)
|
735
|
-
if value.scalar?
|
736
|
-
raise UdatTypeMismatch,
|
737
|
-
"Scalar value found, where a collection was expected."
|
738
|
-
end
|
739
|
-
return value
|
740
|
-
end
|
741
|
-
# Same as UdatCollection#fetch, but raises an error, if the value is
|
742
|
-
# not an UdatScalar.
|
743
|
-
def fetch_scalar(*args)
|
744
|
-
value = fetch(*args)
|
745
|
-
if value.collection?
|
746
|
-
raise UdatTypeMismatch,
|
747
|
-
"Collection found, where a scalar value was expected."
|
748
|
-
end
|
749
|
-
return value
|
750
|
-
end
|
751
|
-
# Returns the first value of the collection, or nil if empty.
|
752
|
-
def first
|
753
|
-
self[0]
|
754
|
-
end
|
755
|
-
# Returns the last value of the collection, or nil if empty.
|
756
|
-
def last
|
757
|
-
self[-1]
|
758
|
-
end
|
759
|
-
|
760
|
-
# Returns the number of values in the collection.
|
761
|
-
def length
|
762
|
-
synchronize do
|
763
|
-
@entries.length
|
837
|
+
# Returns the first value of the collection, or nil if empty.
|
838
|
+
def first
|
839
|
+
self[0]
|
764
840
|
end
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
def empty?
|
769
|
-
length == 0
|
770
|
-
end
|
771
|
-
# Calls a given block for each numeric index.
|
772
|
-
def each_index
|
773
|
-
synchronize do
|
774
|
-
(0...length).each { |i| yield i }
|
841
|
+
# Returns the last value of the collection, or nil if empty.
|
842
|
+
def last
|
843
|
+
self[-1]
|
775
844
|
end
|
776
|
-
return self
|
777
|
-
end
|
778
845
|
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
846
|
+
# Returns the number of values in the collection.
|
847
|
+
def length
|
848
|
+
synchronize do
|
849
|
+
@entries.length
|
850
|
+
end
|
784
851
|
end
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
def values
|
797
|
-
synchronize do
|
798
|
-
return @entries.collect { |key, value| value }
|
852
|
+
alias size length
|
853
|
+
# Returns true, if the collection is empty.
|
854
|
+
def empty?
|
855
|
+
length == 0
|
856
|
+
end
|
857
|
+
# Calls a given block for each numeric index.
|
858
|
+
def each_index
|
859
|
+
synchronize do
|
860
|
+
(0...length).each { |i| yield i }
|
861
|
+
end
|
862
|
+
return self
|
799
863
|
end
|
800
|
-
end
|
801
864
|
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
hash[key] = value unless hash.has_key? key
|
865
|
+
# Returns an Array containing the key/value pairs each as an Array of
|
866
|
+
# size 2. If there is no key, the first element of the sub Array is
|
867
|
+
# nil.
|
868
|
+
def key_value_pairs
|
869
|
+
synchronize do
|
870
|
+
return @entries.collect { |entry| entry.dup }
|
809
871
|
end
|
810
872
|
end
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
873
|
+
# Returns an Array containing all keys of the collection.
|
874
|
+
def keys
|
875
|
+
keys = nil
|
876
|
+
synchronize do
|
877
|
+
keys = @entries.collect { |key, value| key }
|
878
|
+
end
|
879
|
+
keys.compact!
|
880
|
+
return keys
|
881
|
+
end
|
882
|
+
# Returns an Array containing all values of the collection.
|
883
|
+
def values
|
884
|
+
synchronize do
|
885
|
+
return @entries.collect { |key, value| value }
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
# Returns a hash, where each key is mapped to the respective value.
|
890
|
+
def to_hash
|
891
|
+
hash = {}
|
892
|
+
synchronize do
|
893
|
+
@entries.each do |key, value|
|
894
|
+
next if key.nil?
|
895
|
+
hash[key] = value unless hash.has_key? key
|
827
896
|
end
|
828
897
|
end
|
898
|
+
return hash
|
829
899
|
end
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
# another UdatCollection.
|
834
|
-
def replace(other)
|
835
|
-
synchronize do
|
836
|
-
clear
|
837
|
-
concat(other)
|
900
|
+
# Same as Udat::Collection#values.
|
901
|
+
def to_ary
|
902
|
+
values
|
838
903
|
end
|
839
|
-
|
840
|
-
end
|
904
|
+
alias to_a values
|
841
905
|
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
@entries.collect do |key, value|
|
850
|
-
if key
|
851
|
-
"<#{key.encode}>[#{value.encode}]"
|
906
|
+
# Appends the values (and corresponding keys) of another
|
907
|
+
# Udat::Collection and returns self.
|
908
|
+
def concat(other)
|
909
|
+
synchronize do
|
910
|
+
other.key_value_pairs.each do |key, value|
|
911
|
+
if key.nil?
|
912
|
+
append_value(value)
|
852
913
|
else
|
853
|
-
|
914
|
+
append_pair(key, value)
|
854
915
|
end
|
855
|
-
end
|
856
|
-
|
916
|
+
end
|
917
|
+
end
|
918
|
+
return self
|
919
|
+
end
|
920
|
+
# Replaces the values (and corresponding keys) with the values/keys of
|
921
|
+
# another Udat::Collection and returns self.
|
922
|
+
def replace(other)
|
923
|
+
synchronize do
|
924
|
+
clear
|
925
|
+
concat(other)
|
926
|
+
end
|
927
|
+
return self
|
857
928
|
end
|
858
|
-
end
|
859
929
|
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
930
|
+
# Returns the encoded form of the content as a string.
|
931
|
+
# This method is used by Udat::Node#encode_part and will be declared
|
932
|
+
# protected in future.
|
933
|
+
def encoded_content
|
934
|
+
synchronize do
|
935
|
+
return (
|
936
|
+
@entries.empty? ? "~" :
|
937
|
+
@entries.collect do |key, value|
|
938
|
+
if key
|
939
|
+
"<#{key.encode_part}>[#{value.encode_part}]"
|
940
|
+
else
|
941
|
+
"[#{value.encode_part}]"
|
942
|
+
end
|
943
|
+
end.join
|
944
|
+
)
|
945
|
+
end
|
864
946
|
end
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
end
|
871
|
-
# Returns true, if the order of the contents of this collection is to be
|
872
|
-
# ignored for comparisons.
|
873
|
-
def unordered?
|
874
|
-
not self.ordered
|
875
|
-
end
|
876
|
-
# If set to false, the order of the contents of this collection will be
|
877
|
-
# ignored for comparisons.
|
878
|
-
def ordered=(ordered)
|
879
|
-
synchronize do
|
880
|
-
if ordered == false
|
881
|
-
@ordered = false
|
882
|
-
else
|
883
|
-
@ordered = true
|
947
|
+
|
948
|
+
# Same as Udat::Collection#ordered?.
|
949
|
+
def ordered
|
950
|
+
synchronize do
|
951
|
+
return @ordered
|
884
952
|
end
|
885
953
|
end
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
954
|
+
# Returns false, if the order of the contents of this collection is to
|
955
|
+
# be ignored for comparisons.
|
956
|
+
def ordered?
|
957
|
+
self.ordered
|
958
|
+
end
|
959
|
+
# Returns true, if the order of the contents of this collection is to
|
960
|
+
# be ignored for comparisons.
|
961
|
+
def unordered?
|
962
|
+
not self.ordered
|
963
|
+
end
|
964
|
+
# If set to false, the order of the contents of this collection will be
|
965
|
+
# ignored for comparisons.
|
966
|
+
def ordered=(ordered)
|
967
|
+
synchronize do
|
968
|
+
if ordered == false
|
969
|
+
@ordered = false
|
970
|
+
else
|
971
|
+
@ordered = true
|
972
|
+
end
|
973
|
+
end
|
974
|
+
end
|
975
|
+
# Same as Udat::Collection#ordered = false, but returns self.
|
976
|
+
def unordered!
|
977
|
+
self.ordered = false
|
978
|
+
return self
|
979
|
+
end
|
980
|
+
# Same as Udat::Collection#ordered = true, but returns self.
|
981
|
+
def ordered!
|
982
|
+
self.ordered = true
|
983
|
+
return self
|
984
|
+
end
|
897
985
|
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
986
|
+
# Same as Udat::Node#inspect, but in case of unordered collections it
|
987
|
+
# prepends "udat-unordered" instead of just "udat".
|
988
|
+
def inspect
|
989
|
+
if ordered?
|
990
|
+
return super
|
991
|
+
else
|
992
|
+
"udat-unordered#{self.encode_document}"
|
993
|
+
end
|
905
994
|
end
|
906
|
-
end
|
907
995
|
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
996
|
+
# Returns true, if class, tag and content are matching another object.
|
997
|
+
# The order of the content is only significant for comparison, if both
|
998
|
+
# objects have the Udat::Collection#ordered attribute set to true.
|
999
|
+
def ==(other)
|
1000
|
+
return false unless self.class == other.class
|
1001
|
+
if self.unordered? or other.unordered?
|
1002
|
+
return UnorderedWrapper.new(self) ==
|
1003
|
+
UnorderedWrapper.new(other)
|
1004
|
+
else
|
1005
|
+
return super
|
1006
|
+
end
|
915
1007
|
end
|
1008
|
+
|
916
1009
|
end
|
917
1010
|
|
918
|
-
end
|
919
1011
|
|
1012
|
+
# Class of Udat::Node's holding scalar values (stored as a String).
|
1013
|
+
class Scalar < Node
|
920
1014
|
|
921
|
-
|
922
|
-
|
1015
|
+
public_class_method :new
|
1016
|
+
# Creates a new Udat::Scalar with a given tag (which may be nil) and a
|
1017
|
+
# given content, which is transformed to a string (via Object#to_s).
|
1018
|
+
# It is not recommended to use this method. Use Object#to_udat instead.
|
1019
|
+
def initialize(tag, content)
|
1020
|
+
super tag
|
1021
|
+
self.content = content
|
1022
|
+
end
|
923
1023
|
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
self.content = content
|
931
|
-
end
|
1024
|
+
# Content of the scalar (a String).
|
1025
|
+
attr_reader :content
|
1026
|
+
# Sets the content. The content is casted to a string.
|
1027
|
+
def content=(content)
|
1028
|
+
@content = content.to_s
|
1029
|
+
end
|
932
1030
|
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
1031
|
+
# Same as Udat::Scalar#content.
|
1032
|
+
def to_s
|
1033
|
+
content
|
1034
|
+
end
|
1035
|
+
# Returns the content casted to an Integer.
|
1036
|
+
def to_i
|
1037
|
+
content.to_i
|
1038
|
+
end
|
939
1039
|
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
# Returns the content casted to an Integer.
|
945
|
-
def to_i
|
946
|
-
content.to_i
|
947
|
-
end
|
1040
|
+
# Returns true, if the (String representation of the) content is empty.
|
1041
|
+
def empty?
|
1042
|
+
self.to_s.empty?
|
1043
|
+
end
|
948
1044
|
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
1045
|
+
# Returns the encoded form of the content as a string. In this case
|
1046
|
+
# this is simply an escaped version of the String. This method will be
|
1047
|
+
# declared protected in future versions.
|
1048
|
+
def encoded_content
|
1049
|
+
Udat::escape_string(self.to_s)
|
1050
|
+
end
|
953
1051
|
|
954
|
-
# Returns the encoded form of the content as a string.
|
955
|
-
# In this case this is simply an escaped version of the String.
|
956
|
-
# This method is used by Udat#encode, which returns the complete encoding
|
957
|
-
# of the data (including the tag).
|
958
|
-
def encoded_content
|
959
|
-
escape_string(self.to_s)
|
960
1052
|
end
|
961
1053
|
|
962
1054
|
end
|
963
1055
|
|
964
1056
|
|
965
1057
|
class Object
|
966
|
-
# Casts the Object to
|
967
|
-
# Unless overwritten by sub classes, only the string representation
|
968
|
-
# returned by Object#to_s) will be stored as content in the
|
969
|
-
# object.
|
1058
|
+
# Casts the Object to an Udat::Scalar object, optionally with a given
|
1059
|
+
# tag. Unless overwritten by sub classes, only the string representation
|
1060
|
+
# (as returned by Object#to_s) will be stored as content in the
|
1061
|
+
# Udat::Scalar object.
|
970
1062
|
def to_udat(tag = nil)
|
971
|
-
|
1063
|
+
Udat::Scalar.new(tag, self)
|
972
1064
|
end
|
973
1065
|
end
|
974
1066
|
|
975
1067
|
class Array
|
976
|
-
# Casts the Array to an
|
977
|
-
# tag. The
|
978
|
-
# the array may be an Array of size 2, in which case the 2 entries are
|
1068
|
+
# Casts the Array to an Udat::Collection object, optionally with a given
|
1069
|
+
# tag. The Udat::Collection object is marked to be ordered. Each element
|
1070
|
+
# of the array may be an Array of size 2, in which case the 2 entries are
|
979
1071
|
# used as key and value. If an element of the Array is not an Array, it
|
980
1072
|
# will be used as value without an associated key.
|
981
1073
|
def to_udat(tag = nil)
|
982
|
-
|
1074
|
+
Udat::Collection.new(tag, self)
|
983
1075
|
end
|
984
1076
|
end
|
985
1077
|
|
986
1078
|
class Hash
|
987
|
-
# Casts the Hash to an
|
988
|
-
# tag. The
|
1079
|
+
# Casts the Hash to an Udat::Collection object, optionally with a given
|
1080
|
+
# tag. The Udat::Collection object is marked to be unordered.
|
989
1081
|
def to_udat(tag = nil)
|
990
|
-
|
1082
|
+
Udat::Collection.new(tag, self.to_a).unordered!
|
991
1083
|
end
|
992
1084
|
end
|
993
1085
|
|
994
1086
|
class String
|
995
|
-
# Calls Udat.
|
1087
|
+
# Deprecated. Calls Udat::Node.parse_part(self).
|
1088
|
+
# Use String#parse_udat_document instead.
|
996
1089
|
def parse_udat
|
997
|
-
Udat.
|
1090
|
+
Udat::Node.parse_part(self)
|
1091
|
+
end
|
1092
|
+
# Calls Udat::Node.parse_document(self).
|
1093
|
+
def parse_udat_document
|
1094
|
+
Udat::Node.parse_document(self)
|
998
1095
|
end
|
999
1096
|
end
|
1000
1097
|
|
1001
1098
|
class IO
|
1002
|
-
# Calls Udat.read_from_stream(self).
|
1099
|
+
# Calls Udat::Node.read_from_stream(self).
|
1003
1100
|
def read_udat
|
1004
|
-
Udat.read_from_stream(self)
|
1101
|
+
Udat::Node.read_from_stream(self)
|
1005
1102
|
end
|
1006
|
-
# Calls Udat#write_to_stream(self), after converting the object to
|
1007
|
-
# Udat
|
1103
|
+
# Calls Udat::Node#write_to_stream(self), after converting the object to
|
1104
|
+
# an Udat::Node by calling Object#to_udat. Returns self.
|
1008
1105
|
def write_udat(object)
|
1009
1106
|
object.to_udat.write_to_stream(self)
|
1107
|
+
return self
|
1010
1108
|
end
|
1011
1109
|
end
|
1012
1110
|
|
1111
|
+
# Deprecated constant for backwards compatibility.
|
1112
|
+
UdatCollection = Udat::Collection
|
1113
|
+
|
1114
|
+
# Deprecated constant for backwards compatibility.
|
1115
|
+
UdatScalar = Udat::Scalar
|
1116
|
+
|
1117
|
+
|
1118
|
+
|