udat 1.0.0 → 1.1.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/lib/udat.rb +179 -74
- metadata +2 -2
data/lib/udat.rb
CHANGED
@@ -125,9 +125,13 @@ class Udat
|
|
125
125
|
|
126
126
|
# Returns an escaped version of a string, where backslashes are preceding
|
127
127
|
# certain reserved characters.
|
128
|
-
def escape_string(string)
|
128
|
+
def self.escape_string(string)
|
129
129
|
string.to_s.gsub /([<>\[\]|\\])/, "\\\\\\1"
|
130
130
|
end
|
131
|
+
# Calls Udat.escape_string.
|
132
|
+
def escape_string(string)
|
133
|
+
self.class.escape_string(string)
|
134
|
+
end
|
131
135
|
private :escape_string
|
132
136
|
|
133
137
|
# Tag (i.e. type of the content).
|
@@ -179,13 +183,14 @@ class Udat
|
|
179
183
|
def hash
|
180
184
|
to_s.hash
|
181
185
|
end
|
182
|
-
# Returns true, if class, tag and content
|
186
|
+
# Returns true, if class, tag and content (including it's order in case
|
187
|
+
# of a UdatCollection) are matching another object.
|
183
188
|
def eql?(other)
|
184
189
|
self.class == other.class and
|
185
190
|
self.tag == other.tag and
|
186
191
|
self.to_s == other.to_s
|
187
192
|
end
|
188
|
-
# Same as Udat#eql
|
193
|
+
# Same as Udat#eql?, but behaves differently in UdatCollection.
|
189
194
|
def ==(other)
|
190
195
|
self.eql? other
|
191
196
|
end
|
@@ -325,6 +330,56 @@ end
|
|
325
330
|
# methods of this class to Udat objects using Object#to_udat.
|
326
331
|
class UdatCollection < Udat
|
327
332
|
|
333
|
+
# Internally used wrapper class for Udat objects, to lookup
|
334
|
+
# UdatCollection's in Hash'es, while ignoring the order of their content.
|
335
|
+
class UdatUnorderedWrapper
|
336
|
+
def initialize(content)
|
337
|
+
@content = content
|
338
|
+
end
|
339
|
+
attr_accessor :content
|
340
|
+
def hash
|
341
|
+
if content.kind_of? UdatCollection
|
342
|
+
hash = 0
|
343
|
+
content.key_value_pairs.each do |key, value|
|
344
|
+
hash += key.hash
|
345
|
+
hash += value.hash
|
346
|
+
end
|
347
|
+
return hash
|
348
|
+
else
|
349
|
+
return content.hash
|
350
|
+
end
|
351
|
+
end
|
352
|
+
def eql?(other)
|
353
|
+
return false unless self.class == other.class
|
354
|
+
if self.content.class == other.content.class and
|
355
|
+
self.content.tag == other.content.tag
|
356
|
+
if self.content.kind_of? UdatCollection
|
357
|
+
own_pairs = self.content.key_value_pairs
|
358
|
+
other_pairs = other.content.key_value_pairs
|
359
|
+
own_pairs.each do |own_pair|
|
360
|
+
catch :found do
|
361
|
+
other_pairs.each_index do |index|
|
362
|
+
if own_pair.eql? other_pairs[index]
|
363
|
+
other_pairs.delete_at index
|
364
|
+
throw :found
|
365
|
+
end
|
366
|
+
end
|
367
|
+
return false
|
368
|
+
end
|
369
|
+
end
|
370
|
+
return true
|
371
|
+
else
|
372
|
+
return self.content.eql?(other.content)
|
373
|
+
end
|
374
|
+
else
|
375
|
+
return false
|
376
|
+
end
|
377
|
+
end
|
378
|
+
def ==(other)
|
379
|
+
self.eql? other
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
328
383
|
public_class_method :new
|
329
384
|
# Creates a new Udat object with a given tag (which may be nil) and a
|
330
385
|
# given content, represented by a nested Array structure. See the source
|
@@ -333,7 +388,8 @@ class UdatCollection < Udat
|
|
333
388
|
def initialize(tag, content)
|
334
389
|
super tag
|
335
390
|
@ordered = true
|
336
|
-
|
391
|
+
@entries = []
|
392
|
+
require_index_hashes
|
337
393
|
unless content.nil?
|
338
394
|
if content.respond_to? :to_ary
|
339
395
|
content = content.to_ary
|
@@ -345,51 +401,97 @@ class UdatCollection < Udat
|
|
345
401
|
end
|
346
402
|
|
347
403
|
# Deletes the internal index hashes.
|
348
|
-
|
404
|
+
# They are automatically reconstructed once a lookup is done.
|
405
|
+
# This method should be called by the user, if contained Udat objects
|
406
|
+
# have changed, AFTER being added to this collection (similar to
|
407
|
+
# Hash#rehash).
|
408
|
+
def rehash
|
349
409
|
synchronize do
|
350
|
-
@
|
351
|
-
@
|
410
|
+
@key_indicies = nil
|
411
|
+
@value_indicies = nil
|
412
|
+
@unordered_key_indicies = nil
|
413
|
+
@unordered_value_indicies = nil
|
414
|
+
@all_unordered_key_indicies = nil
|
415
|
+
@all_unordered_value_indicies = nil
|
352
416
|
end
|
353
417
|
return self
|
354
418
|
end
|
355
|
-
|
356
|
-
#
|
357
|
-
def
|
419
|
+
|
420
|
+
# Returns true, if the internal index hashes are not existent.
|
421
|
+
def index_hashes_void?
|
358
422
|
synchronize do
|
359
|
-
return @
|
360
|
-
end
|
423
|
+
return @key_indicies.nil?
|
424
|
+
end
|
425
|
+
end
|
426
|
+
private :index_hashes_void?
|
427
|
+
# Registers a key value pair in the index.
|
428
|
+
def add_to_index_hashes(index, key, value)
|
429
|
+
@key_indicies[key] ||= []
|
430
|
+
@key_indicies[key] << index
|
431
|
+
@value_indicies[value] ||= []
|
432
|
+
@value_indicies[value] << index
|
433
|
+
if key.kind_of? UdatCollection and key.unordered?
|
434
|
+
@unordered_key_indicies[UdatUnorderedWrapper.new(key)] ||= []
|
435
|
+
@unordered_key_indicies[UdatUnorderedWrapper.new(key)] << index
|
436
|
+
end
|
437
|
+
if value.kind_of? UdatCollection and value.unordered?
|
438
|
+
@unordered_value_indicies[UdatUnorderedWrapper.new(value)] ||= []
|
439
|
+
@unordered_value_indicies[UdatUnorderedWrapper.new(value)] << index
|
440
|
+
end
|
441
|
+
@all_unordered_key_indicies[UdatUnorderedWrapper.new(key)] ||= []
|
442
|
+
@all_unordered_key_indicies[UdatUnorderedWrapper.new(key)] << index
|
443
|
+
@all_unordered_value_indicies[UdatUnorderedWrapper.new(value)] ||= []
|
444
|
+
@all_unordered_value_indicies[UdatUnorderedWrapper.new(value)] << index
|
445
|
+
return self
|
361
446
|
end
|
447
|
+
private :add_to_index_hashes
|
362
448
|
# Creates index hashes, if they are not existent.
|
363
|
-
def
|
449
|
+
def require_index_hashes
|
364
450
|
synchronize do
|
365
|
-
if
|
366
|
-
@
|
367
|
-
@
|
368
|
-
@
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
451
|
+
if index_hashes_void?
|
452
|
+
@key_indicies = {}
|
453
|
+
@value_indicies = {}
|
454
|
+
@unordered_key_indicies = {}
|
455
|
+
@unordered_value_indicies = {}
|
456
|
+
@all_unordered_key_indicies = {}
|
457
|
+
@all_unordered_value_indicies = {}
|
458
|
+
@entries.each_index do |index|
|
459
|
+
key_value_pair = @entries[index]
|
460
|
+
key = key_value_pair[0]
|
461
|
+
value = key_value_pair[1]
|
462
|
+
add_to_index_hashes(index, key, value) unless key.nil?
|
373
463
|
end
|
374
464
|
end
|
375
465
|
end
|
376
466
|
return self
|
377
467
|
end
|
378
|
-
private :
|
468
|
+
private :require_index_hashes
|
379
469
|
|
380
470
|
# Empties the collection.
|
381
471
|
def clear
|
382
|
-
|
383
|
-
|
384
|
-
|
472
|
+
synchronize do
|
473
|
+
@entries.clear
|
474
|
+
rehash
|
475
|
+
require_index_hashes
|
476
|
+
end
|
385
477
|
return self
|
386
478
|
end
|
479
|
+
# Deletes the entry at the given numeric index.
|
480
|
+
# Returns the value, or nil if the index is out of bounds.
|
481
|
+
def delete_at(index)
|
482
|
+
index = index.to_int
|
483
|
+
synchronize do
|
484
|
+
key_value_pair = @entries.delete_at(index)
|
485
|
+
rehash
|
486
|
+
return key_value_pair ? key_value_pair[0] : nil
|
487
|
+
end
|
488
|
+
end
|
387
489
|
# Deletes all entries with a given key.
|
388
490
|
def delete_key(key)
|
389
491
|
key = key.to_udat
|
390
492
|
synchronize do
|
391
493
|
@entries.delete_if { |e_key, e_value| e_key == key }
|
392
|
-
|
494
|
+
rehash
|
393
495
|
end
|
394
496
|
return self
|
395
497
|
end
|
@@ -398,7 +500,7 @@ class UdatCollection < Udat
|
|
398
500
|
value = value.to_udat
|
399
501
|
synchronize do
|
400
502
|
@entries.delete_if { |e_key, e_value| e_value == value }
|
401
|
-
|
503
|
+
rehash
|
402
504
|
end
|
403
505
|
return self
|
404
506
|
end
|
@@ -408,11 +510,9 @@ class UdatCollection < Udat
|
|
408
510
|
key = key.to_udat
|
409
511
|
value = value.to_udat
|
410
512
|
synchronize do
|
513
|
+
index = @entries.length
|
411
514
|
@entries << [key, value]
|
412
|
-
unless
|
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
|
515
|
+
add_to_index_hashes(index, key, value) unless index_hashes_void?
|
416
516
|
end
|
417
517
|
return self
|
418
518
|
end
|
@@ -450,7 +550,7 @@ class UdatCollection < Udat
|
|
450
550
|
value = value.to_ary
|
451
551
|
unless value.length == 2
|
452
552
|
raise ArgumentError,
|
453
|
-
"#{value.length} array elements found
|
553
|
+
"#{value.length} array elements found where 2 were expected."
|
454
554
|
end
|
455
555
|
return append_pair(value[0], value[1])
|
456
556
|
else
|
@@ -480,20 +580,52 @@ class UdatCollection < Udat
|
|
480
580
|
return entry ? entry[1] : nil
|
481
581
|
end
|
482
582
|
end
|
483
|
-
# Returns
|
484
|
-
def
|
583
|
+
# Returns an Array of values having a given key.
|
584
|
+
def values_by_key(key)
|
585
|
+
key = key.to_udat
|
485
586
|
synchronize do
|
486
|
-
|
487
|
-
|
587
|
+
require_index_hashes
|
588
|
+
if key.kind_of? UdatCollection and key.unordered?
|
589
|
+
indicies = @all_unordered_key_indicies[
|
590
|
+
UdatUnorderedWrapper.new(key)
|
591
|
+
] || []
|
592
|
+
else
|
593
|
+
indicies = (@key_indicies[key] || []) + (
|
594
|
+
@unordered_key_indicies[UdatUnorderedWrapper.new(key)] || []
|
595
|
+
)
|
596
|
+
indicies.uniq!
|
597
|
+
indicies.sort!
|
598
|
+
end
|
599
|
+
return indicies.collect { |index| @entries[index][1] }
|
488
600
|
end
|
489
601
|
end
|
490
|
-
# Returns
|
491
|
-
def
|
602
|
+
# Returns an Array of keys having a given value.
|
603
|
+
def keys_by_value(value)
|
604
|
+
value = value.to_udat
|
492
605
|
synchronize do
|
493
|
-
|
494
|
-
|
606
|
+
require_index_hashes
|
607
|
+
if value.kind_of? UdatCollection and value.unordered?
|
608
|
+
indicies = @all_unordered_value_indicies[
|
609
|
+
UdatUnorderedWrapper.new(value)
|
610
|
+
] || []
|
611
|
+
else
|
612
|
+
indicies = (value_indicies[key] || []) + (
|
613
|
+
@unordered_value_indicies[UdatUnorderedWrapper.new(value)] || []
|
614
|
+
)
|
615
|
+
indicies.uniq!
|
616
|
+
indicies.sort!
|
617
|
+
end
|
618
|
+
return indicies.collect { |index| @entries[index][0] }
|
495
619
|
end
|
496
620
|
end
|
621
|
+
# Returns the last value having a given key.
|
622
|
+
def value_by_key(key)
|
623
|
+
values_by_key(key).last
|
624
|
+
end
|
625
|
+
# Returns the last key having a given value.
|
626
|
+
def key_by_value(value)
|
627
|
+
keys_by_value(value).last
|
628
|
+
end
|
497
629
|
|
498
630
|
# Behaves differently depending on the type of the argument.
|
499
631
|
# If the argument is an Integer the method behaves same as
|
@@ -675,7 +807,6 @@ class UdatCollection < Udat
|
|
675
807
|
# ignored for comparisons.
|
676
808
|
def ordered=(ordered)
|
677
809
|
synchronize do
|
678
|
-
index_void!
|
679
810
|
if ordered == false
|
680
811
|
@ordered = false
|
681
812
|
else
|
@@ -704,40 +835,13 @@ class UdatCollection < Udat
|
|
704
835
|
end
|
705
836
|
end
|
706
837
|
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
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
|
838
|
+
def ==(other)
|
839
|
+
return false unless self.class == other.class
|
840
|
+
if self.unordered? or other.unordered?
|
841
|
+
return UdatUnorderedWrapper.new(self) ==
|
842
|
+
UdatUnorderedWrapper.new(other)
|
739
843
|
else
|
740
|
-
return
|
844
|
+
return super
|
741
845
|
end
|
742
846
|
end
|
743
847
|
|
@@ -758,6 +862,7 @@ class UdatScalar < Udat
|
|
758
862
|
|
759
863
|
# Content of the scalar (a String).
|
760
864
|
attr_reader :content
|
865
|
+
# Sets the content. The content is casted to a string.
|
761
866
|
def content=(content)
|
762
867
|
@content = content.to_s
|
763
868
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: udat
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2007-05-
|
6
|
+
version: 1.1.0
|
7
|
+
date: 2007-05-23 00:00:00 +00:00
|
8
8
|
summary: Parser and generator for UDAT documents, a generic data format similar to XML or YAML.
|
9
9
|
require_paths:
|
10
10
|
- lib/
|