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