udat 1.0.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/LICENSE +23 -0
- data/lib/udat.rb +803 -0
- metadata +51 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (c) 2007 FlexiGuided GmbH, Berlin
|
2
|
+
#
|
3
|
+
# Author: Jan Behrens
|
4
|
+
#
|
5
|
+
# Website: http://www.flexiguided.de/publications.flexirecord.en.html
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
data/lib/udat.rb
ADDED
@@ -0,0 +1,803 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'monitor'
|
4
|
+
|
5
|
+
|
6
|
+
# Copyright (c) 2007 FlexiGuided GmbH, Berlin
|
7
|
+
#
|
8
|
+
# Author: Jan Behrens
|
9
|
+
#
|
10
|
+
# Website: http://www.flexiguided.de/publications.udat.en.html
|
11
|
+
#
|
12
|
+
# -----
|
13
|
+
#
|
14
|
+
# Abstract class of an UDAT object. UDAT objects basically consist of a tag
|
15
|
+
# and the content. The tag can be a String or be nil. The content is either
|
16
|
+
# a scalar value stored as a String, or an ordered/unordered collection of
|
17
|
+
# values (where each value can optionally have a key associated with it).
|
18
|
+
# Keys and values of collections are always UDAT objects too.
|
19
|
+
#
|
20
|
+
# UDAT objects can most easily constructed from other ruby objects by using
|
21
|
+
# the Object#to_udat method.
|
22
|
+
#
|
23
|
+
# By calling the method Udat#encode, the UDAT object is encoded in a both
|
24
|
+
# easily human readable and easily machine readable format. The output can
|
25
|
+
# be later parsed by String#parse_udat.
|
26
|
+
class Udat
|
27
|
+
|
28
|
+
include MonitorMixin
|
29
|
+
|
30
|
+
# A string containing an UDAT example document.
|
31
|
+
ExampleDocument = <<-'END_OF_EXAMPLE'
|
32
|
+
This file contains several UDAT objects (each of them enclosed by
|
33
|
+
square brackets), serving as an example for the UDAT format:
|
34
|
+
|
35
|
+
|
36
|
+
Scalar values:
|
37
|
+
|
38
|
+
[Hello World!]
|
39
|
+
|
40
|
+
[UTF-8|Hello World!] This object has a tag "UTF-8".
|
41
|
+
Intepretation of tags is task of the
|
42
|
+
application, there are no standard tags
|
43
|
+
defined.
|
44
|
+
|
45
|
+
[23]
|
46
|
+
|
47
|
+
[integer|23]
|
48
|
+
|
49
|
+
[rational|2/3]
|
50
|
+
|
51
|
+
|
52
|
+
Arrays (or sets):
|
53
|
+
|
54
|
+
[ [1] [2] [3] ]
|
55
|
+
|
56
|
+
[ [1] [2] Comments are allowed here! (and alomost everywhere) [3] ]
|
57
|
+
|
58
|
+
[ [person| <firstname>[Max] <lastname>[Mustermann] ]
|
59
|
+
[person| <firstname>[Martin] <lastname>[html|Müller] ]
|
60
|
+
[group| <name>[Sample group] <members> [
|
61
|
+
[person| <firstname>[Max] <lastname>[Mustermann] ]
|
62
|
+
[person| <firstname>[Martin] <lastname>[html|Müller] ]
|
63
|
+
] ] ]
|
64
|
+
|
65
|
+
References like in YAML with &id and *id
|
66
|
+
are not directly supported by UDAT.
|
67
|
+
|
68
|
+
|
69
|
+
Ordered or unordered maps:
|
70
|
+
|
71
|
+
[ <&> [html|&]
|
72
|
+
<"> [html|"]
|
73
|
+
<\<> [html|<]
|
74
|
+
<\>> [html|>] ]
|
75
|
+
|
76
|
+
[ <color combination|[red][blue]> [nice]
|
77
|
+
<color combination|[red][magenta]> [ugly] ]
|
78
|
+
|
79
|
+
[ < <red>[on] <yellow>[off] <green>[off] > [stop]
|
80
|
+
< <red>[on] <yellow>[on] <green>[off] > [attention]
|
81
|
+
< <red>[off] <yellow>[off] <green>[on] > [go]
|
82
|
+
< <red>[off] <yellow>[on] <green>[off] > [prepare to stop] ]
|
83
|
+
|
84
|
+
|
85
|
+
Sample configuration file:
|
86
|
+
|
87
|
+
[sample config v1.0|
|
88
|
+
<locale> [
|
89
|
+
<language> [de]
|
90
|
+
<timezone> [CET]
|
91
|
+
]
|
92
|
+
<logging> [
|
93
|
+
<verbosity> [high]
|
94
|
+
<destination> [file|/var/log/sample.log]
|
95
|
+
]
|
96
|
+
<network> [
|
97
|
+
<max connections> [256]
|
98
|
+
<reverse lookups> [yes]
|
99
|
+
]
|
100
|
+
<access control> [
|
101
|
+
<allowed> [ [user|martin] [user|max] [group|admins] ]
|
102
|
+
]
|
103
|
+
]
|
104
|
+
|
105
|
+
|
106
|
+
END_OF_EXAMPLE
|
107
|
+
|
108
|
+
# Error raised by UdatCollection#fetch,
|
109
|
+
# when a fetched value doesn't have the expected tag (i.e. type).
|
110
|
+
class UdatTagMismatch < StandardError; end
|
111
|
+
# Error raised by UdatCollection#fetch,
|
112
|
+
# when a fetched value is a collection instead of a scalar or vice versa.
|
113
|
+
class UdatTypeMismatch < StandardError; end
|
114
|
+
# UDAT parsing error
|
115
|
+
class UdatParseError < StandardError; end
|
116
|
+
|
117
|
+
private_class_method :new
|
118
|
+
# Creates a new Udat object with a given tag (which may be nil).
|
119
|
+
# This method is private,
|
120
|
+
# Udat#initialize is only called through supercalls.
|
121
|
+
def initialize(tag)
|
122
|
+
super()
|
123
|
+
self.tag = tag
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns an escaped version of a string, where backslashes are preceding
|
127
|
+
# certain reserved characters.
|
128
|
+
def escape_string(string)
|
129
|
+
string.to_s.gsub /([<>\[\]|\\])/, "\\\\\\1"
|
130
|
+
end
|
131
|
+
private :escape_string
|
132
|
+
|
133
|
+
# Tag (i.e. type of the content).
|
134
|
+
attr_reader :tag
|
135
|
+
# Sets the tag (i.e. type of the content).
|
136
|
+
def tag=(tag)
|
137
|
+
synchronize do
|
138
|
+
@tag = tag ? tag.to_s : nil
|
139
|
+
end
|
140
|
+
return tag
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns true, if the UDAT object represents a scalar value.
|
144
|
+
def scalar?
|
145
|
+
kind_of? UdatScalar
|
146
|
+
end
|
147
|
+
# Returns true, if the UDAT object represents a collection.
|
148
|
+
def collection?
|
149
|
+
kind_of? UdatCollection
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the data (including it's tag) encoded in the UDAT format.
|
153
|
+
#
|
154
|
+
# Note: The UDAT format doesn't contain information, whether contained
|
155
|
+
# collections are ordered or unordered. This information is lost during
|
156
|
+
# the encoding process, and has to be restored in an application
|
157
|
+
# specific way, if neccessary.
|
158
|
+
def encode
|
159
|
+
synchronize do
|
160
|
+
if tag
|
161
|
+
return "#{escape_string tag}|#{encoded_content}"
|
162
|
+
else
|
163
|
+
return encoded_content
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
# Here the method does the same as Udat#encode, but this method is
|
168
|
+
# overwritten in UdatScalar!
|
169
|
+
def to_s
|
170
|
+
encode
|
171
|
+
end
|
172
|
+
# Does the same as Udat#encode, but encloses the results in curly
|
173
|
+
# brackets and preceds them with the string "udat".
|
174
|
+
def inspect
|
175
|
+
"udat{#{self.encode}}"
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns a hash key used by ruby's Hash'es.
|
179
|
+
def hash
|
180
|
+
to_s.hash
|
181
|
+
end
|
182
|
+
# Returns true, if class, tag and content are matching another object.
|
183
|
+
def eql?(other)
|
184
|
+
self.class == other.class and
|
185
|
+
self.tag == other.tag and
|
186
|
+
self.to_s == other.to_s
|
187
|
+
end
|
188
|
+
# Same as Udat#eql?.
|
189
|
+
def ==(other)
|
190
|
+
self.eql? other
|
191
|
+
end
|
192
|
+
|
193
|
+
# Casts a value to an Udat object. If two arguments are supplied,
|
194
|
+
# the first argument is a tag, while the second is the content.
|
195
|
+
# If just one argument is supplied, then the tag is assumed to be nil,
|
196
|
+
# and the argument given is the content.
|
197
|
+
# Interpretation of the content depends on the type: If the content is
|
198
|
+
# an Array (responds to 'to_ary') an ordered collection will be created,
|
199
|
+
# if the content is a Hash (responds to 'to_hash'), an unordered
|
200
|
+
# collection will be created. Otherwise the content will be casted to a
|
201
|
+
# String with 'to_s' to be stored as a scalar.
|
202
|
+
# If you pass an Array, each element of that array may be an Array
|
203
|
+
# of size 2, in which case the 2 entries are used as key and value.
|
204
|
+
# If an element of the Array is not an Array, it will be used as value
|
205
|
+
# without an associated key.
|
206
|
+
# This method is called by Object#to_udat.
|
207
|
+
def self.construct(*args)
|
208
|
+
if args.length == 1
|
209
|
+
tag = nil
|
210
|
+
content = args[0]
|
211
|
+
elsif args.length == 2
|
212
|
+
tag = args[0]
|
213
|
+
content = args[1]
|
214
|
+
else
|
215
|
+
raise ArgumentError, "Wrong number of arguments supplied."
|
216
|
+
end
|
217
|
+
if content.kind_of? Udat
|
218
|
+
if tag
|
219
|
+
content = content.dup
|
220
|
+
content.tag = tag
|
221
|
+
end
|
222
|
+
return content
|
223
|
+
elsif content.respond_to? :to_ary
|
224
|
+
return UdatCollection.new(tag, content.to_ary)
|
225
|
+
elsif content.respond_to? :to_hash
|
226
|
+
return UdatCollection.new(tag, content.to_hash.to_a).unordered!
|
227
|
+
else
|
228
|
+
return UdatScalar.new(tag, content)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Internal parsing function.
|
233
|
+
def self.parse_intern(input, start_pos = 0)
|
234
|
+
string = ""
|
235
|
+
tag = nil
|
236
|
+
key = nil
|
237
|
+
collection = nil
|
238
|
+
pos = start_pos
|
239
|
+
while pos < input.length
|
240
|
+
char = input[pos, 1]
|
241
|
+
case char
|
242
|
+
when "\\"
|
243
|
+
pos += 1
|
244
|
+
char = input[pos, 1]
|
245
|
+
if char.empty?
|
246
|
+
raise UdatParseError, "Backslash at end of input."
|
247
|
+
end
|
248
|
+
if char =~ /([<>\[\]|\\])/
|
249
|
+
string << char
|
250
|
+
elsif char == "\n"
|
251
|
+
elsif char == "\r"
|
252
|
+
next_char = input[pos + 1, 1]
|
253
|
+
pos += 1 if next_char == "\n"
|
254
|
+
else
|
255
|
+
raise UdatParseError, "Unknown escape sequence found."
|
256
|
+
end
|
257
|
+
pos += 1
|
258
|
+
when "|"
|
259
|
+
if tag
|
260
|
+
raise UdatParseError, "Multiple occurrences of pipe symbol."
|
261
|
+
end
|
262
|
+
tag = string
|
263
|
+
string = ""
|
264
|
+
pos += 1
|
265
|
+
when "<"
|
266
|
+
if key
|
267
|
+
raise UdatParseError, "Unexpected opening angle bracket."
|
268
|
+
end
|
269
|
+
key, pos = parse_intern(input, pos + 1)
|
270
|
+
unless input[pos, 1] == ">"
|
271
|
+
raise ArgumentError, "Closing angle bracket not found."
|
272
|
+
end
|
273
|
+
pos += 1
|
274
|
+
when ">"
|
275
|
+
break
|
276
|
+
when "["
|
277
|
+
value, pos = parse_intern(input, pos + 1)
|
278
|
+
unless input[pos, 1] == "]"
|
279
|
+
raise UdatParseError, "Closing square bracket not found."
|
280
|
+
end
|
281
|
+
collection ||= UdatCollection.new(tag, [])
|
282
|
+
if key
|
283
|
+
collection.append_pair(key, value)
|
284
|
+
key = nil
|
285
|
+
else
|
286
|
+
collection.append_value(value)
|
287
|
+
end
|
288
|
+
pos += 1
|
289
|
+
when "]"
|
290
|
+
break
|
291
|
+
else
|
292
|
+
string << char
|
293
|
+
pos += 1
|
294
|
+
end
|
295
|
+
end
|
296
|
+
raise UdatParseError, "Key without value." if key
|
297
|
+
return (collection ? collection : string.to_udat(tag)), pos
|
298
|
+
end
|
299
|
+
private_class_method :parse_intern
|
300
|
+
|
301
|
+
# Parses a given string and returns a structure of Udat objects.
|
302
|
+
#
|
303
|
+
# Note: When parsing UDAT data, no information is gained, whether
|
304
|
+
# collections are ordered or unordered. After parsing all collections
|
305
|
+
# will be marked as unordered, until changed.
|
306
|
+
def self.parse(input)
|
307
|
+
input = input.to_s
|
308
|
+
result, pos = parse_intern(input)
|
309
|
+
if pos < input.length
|
310
|
+
raise UdatParseError, "Closing bracket without opening bracket."
|
311
|
+
end
|
312
|
+
return result
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
|
318
|
+
# Class of UDAT objects holding an ordered or unordered collection of
|
319
|
+
# values (where each value can optionally have a key associated with it).
|
320
|
+
# Keys and values of collections are always UDAT objects too.
|
321
|
+
# UdatCollection's can be interpreted as maps, arrays, sets or any other
|
322
|
+
# non-scalar data structure.
|
323
|
+
#
|
324
|
+
# Note: Keys and values are always implicitly casted by all instance
|
325
|
+
# methods of this class to Udat objects using Object#to_udat.
|
326
|
+
class UdatCollection < Udat
|
327
|
+
|
328
|
+
public_class_method :new
|
329
|
+
# Creates a new Udat object with a given tag (which may be nil) and a
|
330
|
+
# given content, represented by a nested Array structure. See the source
|
331
|
+
# code for details. It is not recommended to use this method. Use
|
332
|
+
# Object#to_udat instead.
|
333
|
+
def initialize(tag, content)
|
334
|
+
super tag
|
335
|
+
@ordered = true
|
336
|
+
clear
|
337
|
+
unless content.nil?
|
338
|
+
if content.respond_to? :to_ary
|
339
|
+
content = content.to_ary
|
340
|
+
else
|
341
|
+
content = [content]
|
342
|
+
end
|
343
|
+
content.each { |entry| self << entry }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Deletes the internal index hashes.
|
348
|
+
def index_void!
|
349
|
+
synchronize do
|
350
|
+
@key_to_value = nil
|
351
|
+
@value_to_key = nil
|
352
|
+
end
|
353
|
+
return self
|
354
|
+
end
|
355
|
+
# Checks, if the internal index hashes are existent
|
356
|
+
# (and therefore up to date).
|
357
|
+
def index_void?
|
358
|
+
synchronize do
|
359
|
+
return @key_to_value.nil?
|
360
|
+
end
|
361
|
+
end
|
362
|
+
# Creates index hashes, if they are not existent.
|
363
|
+
def require_index
|
364
|
+
synchronize do
|
365
|
+
if index_void?
|
366
|
+
@key_to_value = {}
|
367
|
+
@value_to_key = {}
|
368
|
+
@entries.each do |key, value|
|
369
|
+
unless key.nil?
|
370
|
+
@key_to_value[key] = value unless @key_to_value.has_key? key
|
371
|
+
@value_to_key[value] = key unless @value_to_key.has_key? value
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
return self
|
377
|
+
end
|
378
|
+
private :index_void!, :index_void?, :require_index
|
379
|
+
|
380
|
+
# Empties the collection.
|
381
|
+
def clear
|
382
|
+
@entries = []
|
383
|
+
@key_to_value = {}
|
384
|
+
@value_to_key = {}
|
385
|
+
return self
|
386
|
+
end
|
387
|
+
# Deletes all entries with a given key.
|
388
|
+
def delete_key(key)
|
389
|
+
key = key.to_udat
|
390
|
+
synchronize do
|
391
|
+
@entries.delete_if { |e_key, e_value| e_key == key }
|
392
|
+
index_void!
|
393
|
+
end
|
394
|
+
return self
|
395
|
+
end
|
396
|
+
# Deletes all entries with a given value.
|
397
|
+
def delete_value(value)
|
398
|
+
value = value.to_udat
|
399
|
+
synchronize do
|
400
|
+
@entries.delete_if { |e_key, e_value| e_value == value }
|
401
|
+
index_void!
|
402
|
+
end
|
403
|
+
return self
|
404
|
+
end
|
405
|
+
|
406
|
+
# Appends a new key with a new value.
|
407
|
+
def append_pair(key, value)
|
408
|
+
key = key.to_udat
|
409
|
+
value = value.to_udat
|
410
|
+
synchronize do
|
411
|
+
@entries << [key, value]
|
412
|
+
unless index_void?
|
413
|
+
@key_to_value[key] = value unless @key_to_value.has_key? key
|
414
|
+
@value_to_key[value] = key unless @value_to_key.has_key? value
|
415
|
+
end
|
416
|
+
end
|
417
|
+
return self
|
418
|
+
end
|
419
|
+
# Appends a new value.
|
420
|
+
def append_value(value)
|
421
|
+
value = value.to_udat
|
422
|
+
synchronize do
|
423
|
+
@entries << [nil, value]
|
424
|
+
end
|
425
|
+
return self
|
426
|
+
end
|
427
|
+
# Deletes all key/value pairs where the given key matches,
|
428
|
+
# and appends a new key/value pair.
|
429
|
+
def set_value_for_key(key, value)
|
430
|
+
delete_key(key)
|
431
|
+
append_pair(key, value)
|
432
|
+
return self
|
433
|
+
end
|
434
|
+
# Deletes all values or key/value pairs where the given value matches,
|
435
|
+
# and appends a new key/value pair.
|
436
|
+
def set_key_for_value(key, value)
|
437
|
+
delete_value(value)
|
438
|
+
append_pair(key, value)
|
439
|
+
return self
|
440
|
+
end
|
441
|
+
|
442
|
+
# Behaves differently depending on the type of the argument. If the
|
443
|
+
# argument is an Array (responds to 'to_ary') with 2 elements, a new
|
444
|
+
# key/value pair is added, with the key being the first element of the
|
445
|
+
# array, and the value being the second argument of the array. Arrays
|
446
|
+
# with any other number of elements cause an error. If the argument is
|
447
|
+
# not an array, then it is added as a value without a key.
|
448
|
+
def <<(value)
|
449
|
+
if value.respond_to? :to_ary
|
450
|
+
value = value.to_ary
|
451
|
+
unless value.length == 2
|
452
|
+
raise ArgumentError,
|
453
|
+
"#{value.length} array elements found, while 2 were expected."
|
454
|
+
end
|
455
|
+
return append_pair(value[0], value[1])
|
456
|
+
else
|
457
|
+
return append_value(value)
|
458
|
+
end
|
459
|
+
end
|
460
|
+
# Same as UdatCollection#set_value_for_key.
|
461
|
+
def []=(key, value)
|
462
|
+
set_value_for_key(key, value)
|
463
|
+
end
|
464
|
+
|
465
|
+
# Returns the key at a numeric index, or nil, if the index is out of
|
466
|
+
# bounds or at the given index there is only a value without key.
|
467
|
+
def key_by_index(index)
|
468
|
+
index = index.to_int
|
469
|
+
synchronize do
|
470
|
+
entry = @entries[index]
|
471
|
+
return entry ? entry[0] : nil
|
472
|
+
end
|
473
|
+
end
|
474
|
+
# Returns the value at a numeric index, or nil, if the index is out of
|
475
|
+
# bounds.
|
476
|
+
def value_by_index(index)
|
477
|
+
index = index.to_int
|
478
|
+
synchronize do
|
479
|
+
entry = @entries[index]
|
480
|
+
return entry ? entry[1] : nil
|
481
|
+
end
|
482
|
+
end
|
483
|
+
# Returns the first value having a given key.
|
484
|
+
def value_by_key(key)
|
485
|
+
synchronize do
|
486
|
+
require_index
|
487
|
+
return @key_to_value[key.to_udat]
|
488
|
+
end
|
489
|
+
end
|
490
|
+
# Returns the first key having a given value.
|
491
|
+
def key_by_value(value)
|
492
|
+
synchronize do
|
493
|
+
require_index
|
494
|
+
@value_to_key[value.to_udat]
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# Behaves differently depending on the type of the argument.
|
499
|
+
# If the argument is an Integer the method behaves same as
|
500
|
+
# UdatCollection#value_by_index.
|
501
|
+
# Otherwise the method behaves same as UdatCollection#value_by_key.
|
502
|
+
def [](key)
|
503
|
+
if key.kind_of? Integer
|
504
|
+
return value_by_index(key)
|
505
|
+
else
|
506
|
+
return value_by_key(key)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
# Same as UdatCollection#[], but raises an error, if no value was found.
|
510
|
+
# If an additional second argument is passed, an error will also be
|
511
|
+
# raised if the tag (i.e. type) of the value is not equal to the second
|
512
|
+
# argument.
|
513
|
+
def fetch(*args)
|
514
|
+
if args.length == 1
|
515
|
+
value = self[args[0]]
|
516
|
+
unless value
|
517
|
+
raise IndexError, "Value for the given key or index not found."
|
518
|
+
end
|
519
|
+
return value
|
520
|
+
elsif args.length == 2
|
521
|
+
value = fetch(args[0])
|
522
|
+
unless value.tag == args[1]
|
523
|
+
raise UdatTagMismatch, "UDAT tag mismatch."
|
524
|
+
end
|
525
|
+
return value
|
526
|
+
else
|
527
|
+
raise ArgumentError, "Wrong number of arguments supplied."
|
528
|
+
end
|
529
|
+
end
|
530
|
+
# Same as UdatCollection#fetch, but raises an error, if the value is
|
531
|
+
# not an UdatCollection.
|
532
|
+
def fetch_collection(*args)
|
533
|
+
value = fetch(*args)
|
534
|
+
if value.scalar?
|
535
|
+
raise UdatTypeMismatch,
|
536
|
+
"Scalar value found, where a collection was expected."
|
537
|
+
end
|
538
|
+
return value
|
539
|
+
end
|
540
|
+
# Same as UdatCollection#fetch, but raises an error, if the value is
|
541
|
+
# not an UdatScalar.
|
542
|
+
def fetch_scalar(*args)
|
543
|
+
value = fetch(*args)
|
544
|
+
if value.collection?
|
545
|
+
raise UdatTypeMismatch,
|
546
|
+
"Collection found, where a scalar value was expected."
|
547
|
+
end
|
548
|
+
return value
|
549
|
+
end
|
550
|
+
# Returns the first value of the collection, or nil if empty.
|
551
|
+
def first
|
552
|
+
self[0]
|
553
|
+
end
|
554
|
+
# Returns the last value of the collection, or nil if empty.
|
555
|
+
def last
|
556
|
+
self[-1]
|
557
|
+
end
|
558
|
+
|
559
|
+
# Returns the number of values in the collection.
|
560
|
+
def length
|
561
|
+
synchronize do
|
562
|
+
@entries.length
|
563
|
+
end
|
564
|
+
end
|
565
|
+
alias size length
|
566
|
+
# Returns true, if the collection is empty.
|
567
|
+
def empty?
|
568
|
+
length == 0
|
569
|
+
end
|
570
|
+
# Calls a given block for each numeric index.
|
571
|
+
def each_index
|
572
|
+
synchronize do
|
573
|
+
(0...length).each { |i| yield i }
|
574
|
+
end
|
575
|
+
return self
|
576
|
+
end
|
577
|
+
|
578
|
+
# Returns an Array containing the key/value pairs each as an Array of
|
579
|
+
# size 2. If there is no key, the first element of the sub Array is nil.
|
580
|
+
def key_value_pairs
|
581
|
+
synchronize do
|
582
|
+
return @entries.collect { |entry| entry.dup }
|
583
|
+
end
|
584
|
+
end
|
585
|
+
# Returns an Array containing all keys of the collection.
|
586
|
+
def keys
|
587
|
+
keys = nil
|
588
|
+
synchronize do
|
589
|
+
keys = @entries.collect { |key, value| key }
|
590
|
+
end
|
591
|
+
keys.compact!
|
592
|
+
return keys
|
593
|
+
end
|
594
|
+
# Returns an Array containing all values of the collection.
|
595
|
+
def values
|
596
|
+
synchronize do
|
597
|
+
return @entries.collect { |key, value| value }
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# Returns a hash, where each key is mapped to the respective value.
|
602
|
+
def to_hash
|
603
|
+
hash = {}
|
604
|
+
synchronize do
|
605
|
+
@entries.each do |key, value|
|
606
|
+
next if key.nil?
|
607
|
+
hash[key] = value unless hash.has_key? key
|
608
|
+
end
|
609
|
+
end
|
610
|
+
return hash
|
611
|
+
end
|
612
|
+
# Same as UdatCollection#values.
|
613
|
+
def to_ary
|
614
|
+
values
|
615
|
+
end
|
616
|
+
alias to_a values
|
617
|
+
|
618
|
+
# Appends the values (and corresponding keys) of another UdatCollection.
|
619
|
+
def concat(other)
|
620
|
+
synchronize do
|
621
|
+
other.key_value_pairs.each do |key, value|
|
622
|
+
if key.nil?
|
623
|
+
append_value(value)
|
624
|
+
else
|
625
|
+
append_pair(key, value)
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
return self
|
630
|
+
end
|
631
|
+
# Replaces the values (and corresponding keys) with the values/keys of
|
632
|
+
# another UdatCollection.
|
633
|
+
def replace(other)
|
634
|
+
synchronize do
|
635
|
+
clear
|
636
|
+
concat(other)
|
637
|
+
end
|
638
|
+
return self
|
639
|
+
end
|
640
|
+
|
641
|
+
# Returns the encoded form of the content as a string.
|
642
|
+
# This method is used by Udat#encode, which returns the complete encoding
|
643
|
+
# of the data (including the tag).
|
644
|
+
def encoded_content
|
645
|
+
synchronize do
|
646
|
+
return (
|
647
|
+
@entries.collect do |key, value|
|
648
|
+
if key
|
649
|
+
"<#{key.encode}>[#{value.encode}]"
|
650
|
+
else
|
651
|
+
"[#{value.encode}]"
|
652
|
+
end
|
653
|
+
end.join
|
654
|
+
)
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
# Same as UdatCollection#ordered?.
|
659
|
+
def ordered
|
660
|
+
synchronize do
|
661
|
+
return @ordered
|
662
|
+
end
|
663
|
+
end
|
664
|
+
# Returns false, if the order of the contents of this collection is to be
|
665
|
+
# ignored for comparisons.
|
666
|
+
def ordered?
|
667
|
+
self.ordered
|
668
|
+
end
|
669
|
+
# Returns true, if the order of the contents of this collection is to be
|
670
|
+
# ignored for comparisons.
|
671
|
+
def unordered?
|
672
|
+
not self.ordered
|
673
|
+
end
|
674
|
+
# If set to false, the order of the contents of this collection will be
|
675
|
+
# ignored for comparisons.
|
676
|
+
def ordered=(ordered)
|
677
|
+
synchronize do
|
678
|
+
index_void!
|
679
|
+
if ordered == false
|
680
|
+
@ordered = false
|
681
|
+
else
|
682
|
+
@ordered = true
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
# Same as UdatCollection#ordered = false, but returns self.
|
687
|
+
def unordered!
|
688
|
+
self.ordered = false
|
689
|
+
return self
|
690
|
+
end
|
691
|
+
# Same as UdatCollection#ordered = true, but returns self.
|
692
|
+
def ordered!
|
693
|
+
self.ordered = true
|
694
|
+
return self
|
695
|
+
end
|
696
|
+
|
697
|
+
# Same as Udat#inspect, but in case of unordered collections it prepends
|
698
|
+
# "udat-unordered" instead of just "udat".
|
699
|
+
def inspect
|
700
|
+
if ordered?
|
701
|
+
return super
|
702
|
+
else
|
703
|
+
"udat-unordered{#{self.encode}}"
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
# Returns a hash key used by ruby's Hash'es.
|
708
|
+
def hash
|
709
|
+
hash = 0
|
710
|
+
@entries.each do |key, value|
|
711
|
+
hash += key.hash
|
712
|
+
hash += value.hash
|
713
|
+
end
|
714
|
+
return hash
|
715
|
+
end
|
716
|
+
# Returns true, if class, tag and content are matching another object.
|
717
|
+
# The order of the content is ignored, if this or the other object has
|
718
|
+
# the 'ordered' attribute set to false.
|
719
|
+
def eql?(other)
|
720
|
+
if self.class == other.class and self.tag == other.tag
|
721
|
+
if self.ordered? and other.ordered?
|
722
|
+
return self.to_s == other.to_s
|
723
|
+
else
|
724
|
+
own_pairs = self.key_value_pairs
|
725
|
+
other_pairs = other.key_value_pairs
|
726
|
+
own_pairs.each do |own_pair|
|
727
|
+
catch :found do
|
728
|
+
other_pairs.each_index do |index|
|
729
|
+
if own_pair.eql? other_pairs[index]
|
730
|
+
other_pairs.delete_at index
|
731
|
+
throw :found
|
732
|
+
end
|
733
|
+
end
|
734
|
+
return false
|
735
|
+
end
|
736
|
+
end
|
737
|
+
return true
|
738
|
+
end
|
739
|
+
else
|
740
|
+
return false
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
end
|
745
|
+
|
746
|
+
|
747
|
+
# Class of UDAT objects holding scalar values (stored as a String).
|
748
|
+
class UdatScalar < Udat
|
749
|
+
|
750
|
+
public_class_method :new
|
751
|
+
# Creates a new Udat object with a given tag (which may be nil) and a
|
752
|
+
# given content, which is transformed to a string (via 'to_s').
|
753
|
+
# It is not recommended to use this method. Use Object#to_udat instead.
|
754
|
+
def initialize(tag, content)
|
755
|
+
super tag
|
756
|
+
self.content = content
|
757
|
+
end
|
758
|
+
|
759
|
+
# Content of the scalar (a String).
|
760
|
+
attr_reader :content
|
761
|
+
def content=(content)
|
762
|
+
@content = content.to_s
|
763
|
+
end
|
764
|
+
|
765
|
+
# Same as UdatScalar#content.
|
766
|
+
def to_s
|
767
|
+
content
|
768
|
+
end
|
769
|
+
# Returns the content casted to an Integer.
|
770
|
+
def to_i
|
771
|
+
content.to_i
|
772
|
+
end
|
773
|
+
|
774
|
+
# Returns true, if the (String representation of the) content is empty.
|
775
|
+
def empty?
|
776
|
+
self.to_s.empty?
|
777
|
+
end
|
778
|
+
|
779
|
+
# Returns the encoded form of the content as a string.
|
780
|
+
# In this case this is simply an escaped version of the String.
|
781
|
+
# This method is used by Udat#encode, which returns the complete encoding
|
782
|
+
# of the data (including the tag).
|
783
|
+
def encoded_content
|
784
|
+
escape_string(self.to_s)
|
785
|
+
end
|
786
|
+
|
787
|
+
end
|
788
|
+
|
789
|
+
|
790
|
+
class Object
|
791
|
+
# Calls Udat.construct(tag, self).
|
792
|
+
def to_udat(tag = nil)
|
793
|
+
Udat.construct(tag, self)
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
class String
|
798
|
+
# Calls Udat.parse(self).
|
799
|
+
def parse_udat
|
800
|
+
Udat.parse(self)
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: udat
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2007-05-22 00:00:00 +00:00
|
8
|
+
summary: Parser and generator for UDAT documents, a generic data format similar to XML or YAML.
|
9
|
+
require_paths:
|
10
|
+
- lib/
|
11
|
+
email: jan.behrens@flexiguided.de
|
12
|
+
homepage: http://www.flexiguided.de/publications.udat.en.html
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: udat
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Jan Behrens
|
31
|
+
files:
|
32
|
+
- ./LICENSE
|
33
|
+
- lib/udat.rb
|
34
|
+
test_files: []
|
35
|
+
|
36
|
+
rdoc_options:
|
37
|
+
- --title
|
38
|
+
- UDAT Documentation
|
39
|
+
- --main
|
40
|
+
- Udat
|
41
|
+
- --line-numbers
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
executables: []
|
45
|
+
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
requirements: []
|
49
|
+
|
50
|
+
dependencies: []
|
51
|
+
|