udat 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|