Class: UdatCollection

Inherits:
Udat show all
Defined in:
lib/udat.rb

Overview

Class of UDAT objects holding an ordered or unordered collection of values (where each value can optionally have a key associated with it). Keys and values of collections are always UDAT objects too. UdatCollection’s can be interpreted as maps, arrays, sets or any other non-scalar data structure.

Note: Keys and values are always implicitly casted by all instance methods of this class to Udat objects using Object#to_udat.

Defined Under Namespace

Classes: UdatUnorderedWrapper

Constant Summary

Constants inherited from Udat

Udat::ExampleDocument, Udat::KeepTag

Instance Attribute Summary

Attributes inherited from Udat

#tag

Instance Method Summary collapse

Methods inherited from Udat

#collection?, #encode, #eql?, escape_string, #hash, parse, read_from_stream, #scalar?, #to_s, #to_udat, #write_to_stream

Constructor Details

#initialize(tag, content) ⇒ UdatCollection

Creates a new Udat object with a given tag (which may be nil) and a given content, represented by a nested Array structure. See the source code for details. It is not recommended to use this method. Use Object#to_udat instead.



457
458
459
460
461
462
463
# File 'lib/udat.rb', line 457

def initialize(tag, content)
  super tag
  @ordered = true
  @entries = []
  require_index_hashes
  content.to_ary.each { |entry| self << entry }
end

Instance Method Details

#<<(value) ⇒ Object

Behaves differently depending on the type of the argument. If the argument is an Array with 2 elements, a new key/value pair is added, with the key being the first element of the array, and the value being the second argument of the array. Arrays with any other number of elements cause an error. If the argument is not an array, then it is added as a value without a key.



610
611
612
613
614
615
616
617
618
619
620
# File 'lib/udat.rb', line 610

def <<(value)
  if value.kind_of? Array
    unless value.length == 2
      raise ArgumentError,
        "#{value.length} array elements found where 2 were expected."
    end
    return append_pair(value[0], value[1])
  else
    return append_value(value)
  end
end

#==(other) ⇒ Object



900
901
902
903
904
905
906
907
908
# File 'lib/udat.rb', line 900

def ==(other)
  return false unless self.class == other.class
  if self.unordered? or other.unordered?
    return UdatUnorderedWrapper.new(self) ==
      UdatUnorderedWrapper.new(other)
  else
    return super
  end
end

#[](key) ⇒ Object

Behaves differently depending on the type of the argument. If the argument is an Integer the method behaves same as UdatCollection#value_by_index. Otherwise the method behaves same as UdatCollection#value_by_key.



695
696
697
698
699
700
701
# File 'lib/udat.rb', line 695

def [](key)
  if key.kind_of? Integer
    return value_by_index(key)
  else
    return value_by_key(key)
  end
end

#[]=(key, value) ⇒ Object

Same as UdatCollection#set_value_for_key.



622
623
624
# File 'lib/udat.rb', line 622

def []=(key, value)
  set_value_for_key(key, value)
end

#append_pair(key, value) ⇒ Object

Appends a new key with a new value.



571
572
573
574
575
576
577
578
579
580
# File 'lib/udat.rb', line 571

def append_pair(key, value)
  key = key.to_udat
  value = value.to_udat
  synchronize do
    index = @entries.length
    @entries << [key, value]
    add_to_index_hashes(index, key, value) unless index_hashes_void?
  end
  return self
end

#append_value(value) ⇒ Object

Appends a new value.



582
583
584
585
586
587
588
# File 'lib/udat.rb', line 582

def append_value(value)
  value = value.to_udat
  synchronize do
    @entries << [nil, value]
  end
  return self
end

#clearObject

Empties the collection.



533
534
535
536
537
538
539
540
# File 'lib/udat.rb', line 533

def clear
  synchronize do
    @entries.clear
    rehash
    require_index_hashes
  end
  return self
end

#concat(other) ⇒ Object

Appends the values (and corresponding keys) of another UdatCollection.



812
813
814
815
816
817
818
819
820
821
822
823
# File 'lib/udat.rb', line 812

def concat(other)
  synchronize do
    other.key_value_pairs.each do |key, value|
      if key.nil?
        append_value(value)
      else
        append_pair(key, value)
      end
    end
  end
  return self
end

#delete_at(index) ⇒ Object

Deletes the entry at the given numeric index. Returns the value, or nil if the index is out of bounds.



543
544
545
546
547
548
549
550
# File 'lib/udat.rb', line 543

def delete_at(index)
  index = index.to_int
  synchronize do
    key_value_pair = @entries.delete_at(index)
    rehash
    return key_value_pair ? key_value_pair[0] : nil
  end
end

#delete_key(key) ⇒ Object

Deletes all entries with a given key.



552
553
554
555
556
557
558
559
# File 'lib/udat.rb', line 552

def delete_key(key)
  key = key.to_udat
  synchronize do
    @entries.delete_if { |e_key, e_value| e_key == key }
    rehash
  end
  return self
end

#delete_value(value) ⇒ Object

Deletes all entries with a given value.



561
562
563
564
565
566
567
568
# File 'lib/udat.rb', line 561

def delete_value(value)
  value = value.to_udat
  synchronize do
    @entries.delete_if { |e_key, e_value| e_value == value }
    rehash
  end
  return self
end

#each_indexObject

Calls a given block for each numeric index.



764
765
766
767
768
769
# File 'lib/udat.rb', line 764

def each_index
  synchronize do
    (0...length).each { |i| yield i }
  end
  return self
end

#empty?Boolean

Returns true, if the collection is empty.

Returns:

  • (Boolean)


760
761
762
# File 'lib/udat.rb', line 760

def empty?
  length == 0
end

#encoded_contentObject

Returns the encoded form of the content as a string. This method is used by Udat#encode, which returns the complete encoding of the data (including the tag).



837
838
839
840
841
842
843
844
845
846
847
848
849
850
# File 'lib/udat.rb', line 837

def encoded_content
  synchronize do
    return (
      @entries.empty? ? "~" :
      @entries.collect do |key, value|
        if key
          "<#{key.encode}>[#{value.encode}]"
        else
          "[#{value.encode}]"
        end
      end.join
    )
  end
end

#fetch(*args) ⇒ Object

Same as UdatCollection#[], but raises an error, if no value was found. If an additional second argument is passed, an error will also be raised if the tag (i.e. type) of the value is not equal to the second argument.



706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/udat.rb', line 706

def fetch(*args)
  if args.length == 1
    value = self[args[0]]
    unless value
      raise IndexError, "Value for the given key or index not found."
    end
    return value
  elsif args.length == 2
    value = fetch(args[0])
    unless value.tag == args[1]
      raise UdatTagMismatch, "UDAT tag mismatch."
    end
    return value
  else
    raise ArgumentError, "Wrong number of arguments supplied."
  end
end

#fetch_collection(*args) ⇒ Object

Same as UdatCollection#fetch, but raises an error, if the value is not an UdatCollection.



725
726
727
728
729
730
731
732
# File 'lib/udat.rb', line 725

def fetch_collection(*args)
  value = fetch(*args)
  if value.scalar?
    raise UdatTypeMismatch,
      "Scalar value found, where a collection was expected."
  end
  return value
end

#fetch_scalar(*args) ⇒ Object

Same as UdatCollection#fetch, but raises an error, if the value is not an UdatScalar.



735
736
737
738
739
740
741
742
# File 'lib/udat.rb', line 735

def fetch_scalar(*args)
  value = fetch(*args)
  if value.collection?
    raise UdatTypeMismatch,
      "Collection found, where a scalar value was expected."
  end
  return value
end

#firstObject

Returns the first value of the collection, or nil if empty.



744
745
746
# File 'lib/udat.rb', line 744

def first
  self[0]
end

#inspectObject

Same as Udat#inspect, but in case of unordered collections it prepends “udat-unordered” instead of just “udat”.



892
893
894
895
896
897
898
# File 'lib/udat.rb', line 892

def inspect
  if ordered?
    return super
  else
    "udat-unordered{#{self.encode}}"
  end
end

#key_by_index(index) ⇒ Object

Returns the key at a numeric index, or nil, if the index is out of bounds or at the given index there is only a value without key.



628
629
630
631
632
633
634
# File 'lib/udat.rb', line 628

def key_by_index(index)
  index = index.to_int
  synchronize do
    entry = @entries[index]
    return entry ? entry[0] : nil
  end
end

#key_by_value(value) ⇒ Object

Returns the last key having a given value.



687
688
689
# File 'lib/udat.rb', line 687

def key_by_value(value)
  keys_by_value(value).last
end

#key_value_pairsObject

Returns an Array containing the key/value pairs each as an Array of size 2. If there is no key, the first element of the sub Array is nil.



773
774
775
776
777
# File 'lib/udat.rb', line 773

def key_value_pairs
  synchronize do
    return @entries.collect { |entry| entry.dup }
  end
end

#keysObject

Returns an Array containing all keys of the collection.



779
780
781
782
783
784
785
786
# File 'lib/udat.rb', line 779

def keys
  keys = nil
  synchronize do
    keys = @entries.collect { |key, value| key }
  end
  keys.compact!
  return keys
end

#keys_by_value(value) ⇒ Object

Returns an Array of keys having a given value.



664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
# File 'lib/udat.rb', line 664

def keys_by_value(value)
  value = value.to_udat
  synchronize do
    require_index_hashes
    if value.kind_of? UdatCollection and value.unordered?
      indicies = @all_unordered_value_indicies[
        UdatUnorderedWrapper.new(value)
      ] || []
    else
      indicies = (value_indicies[key] || []) + (
        @unordered_value_indicies[UdatUnorderedWrapper.new(value)] || []
      )
      indicies.uniq!
      indicies.sort!
    end
    return indicies.collect { |index| @entries[index][0] }
  end
end

#lastObject

Returns the last value of the collection, or nil if empty.



748
749
750
# File 'lib/udat.rb', line 748

def last
  self[-1]
end

#lengthObject Also known as: size

Returns the number of values in the collection.



753
754
755
756
757
# File 'lib/udat.rb', line 753

def length
  synchronize do
    @entries.length
  end
end

#orderedObject

Same as UdatCollection#ordered?.



853
854
855
856
857
# File 'lib/udat.rb', line 853

def ordered
  synchronize do
    return @ordered
  end
end

#ordered!Object

Same as UdatCollection#ordered = true, but returns self.



885
886
887
888
# File 'lib/udat.rb', line 885

def ordered!
  self.ordered = true
  return self
end

#ordered=(ordered) ⇒ Object

If set to false, the order of the contents of this collection will be ignored for comparisons.



870
871
872
873
874
875
876
877
878
# File 'lib/udat.rb', line 870

def ordered=(ordered)
  synchronize do
    if ordered == false
      @ordered = false
    else
      @ordered = true
    end
  end
end

#ordered?Boolean

Returns false, if the order of the contents of this collection is to be ignored for comparisons.

Returns:

  • (Boolean)


860
861
862
# File 'lib/udat.rb', line 860

def ordered?
  self.ordered
end

#rehashObject

Deletes the internal index hashes. They are automatically reconstructed once a lookup is done. This method should be called by the user, if contained Udat objects have changed, AFTER being added to this collection (similar to Hash#rehash).



470
471
472
473
474
475
476
477
478
479
480
# File 'lib/udat.rb', line 470

def rehash
  synchronize do
    @key_indicies = nil
    @value_indicies = nil
    @unordered_key_indicies = nil
    @unordered_value_indicies = nil
    @all_unordered_key_indicies = nil
    @all_unordered_value_indicies = nil
  end
  return self
end

#replace(other) ⇒ Object

Replaces the values (and corresponding keys) with the values/keys of another UdatCollection.



826
827
828
829
830
831
832
# File 'lib/udat.rb', line 826

def replace(other)
  synchronize do
    clear
    concat(other)
  end
  return self
end

#set_key_for_value(key, value) ⇒ Object

Deletes all values or key/value pairs where the given value matches, and appends a new key/value pair.



598
599
600
601
602
# File 'lib/udat.rb', line 598

def set_key_for_value(key, value)
  delete_value(value)
  append_pair(key, value)
  return self
end

#set_value_for_key(key, value) ⇒ Object

Deletes all key/value pairs where the given key matches, and appends a new key/value pair.



591
592
593
594
595
# File 'lib/udat.rb', line 591

def set_value_for_key(key, value)
  delete_key(key)
  append_pair(key, value)
  return self
end

#to_aryObject

Same as UdatCollection#values.



806
807
808
# File 'lib/udat.rb', line 806

def to_ary
  values
end

#to_hashObject

Returns a hash, where each key is mapped to the respective value.



795
796
797
798
799
800
801
802
803
804
# File 'lib/udat.rb', line 795

def to_hash
  hash = {}
  synchronize do
    @entries.each do |key, value|
      next if key.nil?
      hash[key] = value unless hash.has_key? key
    end
  end
  return hash
end

#unordered!Object

Same as UdatCollection#ordered = false, but returns self.



880
881
882
883
# File 'lib/udat.rb', line 880

def unordered!
  self.ordered = false
  return self
end

#unordered?Boolean

Returns true, if the order of the contents of this collection is to be ignored for comparisons.

Returns:

  • (Boolean)


865
866
867
# File 'lib/udat.rb', line 865

def unordered?
  not self.ordered
end

#value_by_index(index) ⇒ Object

Returns the value at a numeric index, or nil, if the index is out of bounds.



637
638
639
640
641
642
643
# File 'lib/udat.rb', line 637

def value_by_index(index)
  index = index.to_int
  synchronize do
    entry = @entries[index]
    return entry ? entry[1] : nil
  end
end

#value_by_key(key) ⇒ Object

Returns the last value having a given key.



683
684
685
# File 'lib/udat.rb', line 683

def value_by_key(key)
  values_by_key(key).last
end

#valuesObject Also known as: to_a

Returns an Array containing all values of the collection.



788
789
790
791
792
# File 'lib/udat.rb', line 788

def values
  synchronize do
    return @entries.collect { |key, value| value }
  end
end

#values_by_key(key) ⇒ Object

Returns an Array of values having a given key.



645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
# File 'lib/udat.rb', line 645

def values_by_key(key)
  key = key.to_udat
  synchronize do
    require_index_hashes
    if key.kind_of? UdatCollection and key.unordered?
      indicies = @all_unordered_key_indicies[
        UdatUnorderedWrapper.new(key)
      ] || []
    else
      indicies = (@key_indicies[key] || []) + (
        @unordered_key_indicies[UdatUnorderedWrapper.new(key)] || []
      )
      indicies.uniq!
      indicies.sort!
    end
    return indicies.collect { |index| @entries[index][1] }
  end
end