Class: Udat::Collection

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

Overview

Class of Udat::Node’s 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::Node’s too. Udat::Collection’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::Node’s using Object#to_udat.

Defined Under Namespace

Classes: UnorderedWrapper

Instance Attribute Summary

Attributes inherited from Node

#tag

Instance Method Summary collapse

Methods inherited from Node

#encode, #encode_document, #eql?, #hash, parse, parse_document, read_from_stream, #require_collection, #require_scalar, #require_tag, #rpc, #scalar?, #to_s, #to_udat, #write_to_stream

Constructor Details

#initialize(tag, content) ⇒ Collection

Creates a new Udat::Collection with a given tag (which may be nil) and a given content, represented by a nested Array structure. It is not recommended to use this method. Use Object#to_udat instead.



541
542
543
544
545
546
547
# File 'lib/udat.rb', line 541

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.



713
714
715
716
717
718
719
720
721
722
723
# File 'lib/udat.rb', line 713

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

Returns true, if class, tag and content are matching another object. The order of the content is only significant for comparison, if both objects have the Udat::Collection#ordered attribute set to true.



1027
1028
1029
1030
1031
1032
1033
1034
1035
# File 'lib/udat.rb', line 1027

def ==(other)
  return false unless self.class == other.class
  if self.unordered? or other.unordered?
    return UnorderedWrapper.new(self) ==
      UnorderedWrapper.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 Udat::Collection#value_by_index. If the argument is nil, the method behaves like Udat::Collection#value_without_key. Otherwise the method behaves same as Udat::Collection#value_by_key.



829
830
831
832
833
834
835
836
837
# File 'lib/udat.rb', line 829

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

#[]=(key, value) ⇒ Object

Same as Udat::Collection#set_value_for_key.



725
726
727
# File 'lib/udat.rb', line 725

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

#append_pair(key, value) ⇒ Object

Appends a new key with a new value. Returns self.



672
673
674
675
676
677
678
679
680
681
# File 'lib/udat.rb', line 672

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)
  end
  return self
end

#append_value(value) ⇒ Object

Appends a new value. Returns self.



683
684
685
686
687
688
689
690
691
# File 'lib/udat.rb', line 683

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

#clearObject

Empties the collection. Returns self.



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

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

#collection?Boolean

Returns true.

Returns:

  • (Boolean)


550
551
552
# File 'lib/udat.rb', line 550

def collection?
  true
end

#concat(other) ⇒ Object

Appends the values (and corresponding keys) of another Udat::Collection and returns self.



937
938
939
940
941
942
943
944
945
946
947
948
# File 'lib/udat.rb', line 937

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.



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

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[1] : nil
  end
end

#delete_key(key) ⇒ Object

Deletes all entries with a given key. Returns self.



653
654
655
656
657
658
659
660
# File 'lib/udat.rb', line 653

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. Returns self.



662
663
664
665
666
667
668
669
# File 'lib/udat.rb', line 662

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.



887
888
889
890
891
892
# File 'lib/udat.rb', line 887

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)


883
884
885
# File 'lib/udat.rb', line 883

def empty?
  length == 0
end

#encode_contentObject

Returns the encoded form of the content as a string. This method is used by Udat::Node#encode_without_brackets.



999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
# File 'lib/udat.rb', line 999

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

#fetch(key, tag = AnyTag) ⇒ Object

Same as Udat::Collection#[], but raises an Udat::UdatIndexError, 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.



842
843
844
845
846
847
848
849
# File 'lib/udat.rb', line 842

def fetch(key, tag = AnyTag)
  value = self[key]
  unless value
    raise UdatIndexError, "UDAT node not found."
  end
  value.require_tag(tag) unless tag == AnyTag
  return value
end

#fetch_collection(key, tag = AnyTag) ⇒ Object

Same as Udat::Collection#fetch, but raises an error, if the value is not an Udat::Collection.



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

def fetch_collection(key, tag = AnyTag)
  value = fetch(key)
  value.require_collection
  value.require_tag(tag) unless tag == AnyTag
  return value
end

#fetch_scalar(key, tag = AnyTag) ⇒ Object

Same as Udat::Collection#fetch, but raises an error, if the value is not an Udat::Scalar.



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

def fetch_scalar(key, tag = AnyTag)
  value = fetch(key)
  value.require_scalar
  value.require_tag(tag) unless tag == AnyTag
  return value
end

#firstObject

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



867
868
869
# File 'lib/udat.rb', line 867

def first
  value_by_index(0)
end

#include?(value) ⇒ Boolean

Returns true, if the value is contained in the collection.

Returns:

  • (Boolean)


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

def include?(value)
  value = value.to_udat
  synchronize do
    require_index_hashes
    if value.kind_of? Collection and value.unordered?
      return (@all_unordered_value_indicies[
        UnorderedWrapper.new(value)
      ] || []).empty? ? false : true
    else
      return ((@value_indicies[value] || []) + (
        @unordered_value_indicies[UnorderedWrapper.new(value)] || []
      )).empty? ? false : true
    end
  end
end

#inspectObject

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



1016
1017
1018
1019
1020
1021
1022
# File 'lib/udat.rb', line 1016

def inspect
  if ordered?
    return super
  else
    "udat-unordered#{self.encode_document}"
  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.



740
741
742
743
744
745
746
# File 'lib/udat.rb', line 740

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.



804
805
806
# File 'lib/udat.rb', line 804

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.



897
898
899
900
901
# File 'lib/udat.rb', line 897

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

#keysObject

Returns an Array containing all keys of the collection.



903
904
905
906
907
908
909
910
# File 'lib/udat.rb', line 903

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. The Array may contain nil’s for values having no key.



777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
# File 'lib/udat.rb', line 777

def keys_by_value(value)
  value = value.to_udat
  synchronize do
    require_index_hashes
    if value.kind_of? Collection and value.unordered?
      indicies = @all_unordered_value_indicies[
        UnorderedWrapper.new(value)
      ] || []
    else
      indicies = (@value_indicies[value] || []) + (
        @unordered_value_indicies[UnorderedWrapper.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.



871
872
873
# File 'lib/udat.rb', line 871

def last
  value_by_index(-1)
end

#lengthObject Also known as: size

Returns the number of values in the collection.



876
877
878
879
880
# File 'lib/udat.rb', line 876

def length
  synchronize do
    @entries.length
  end
end

#orderedObject

Same as Udat::Collection#ordered?.



960
961
962
963
964
# File 'lib/udat.rb', line 960

def ordered
  synchronize do
    return @ordered
  end
end

#ordered!Object

Same as Udat::Collection#ordered = true, but returns self.



992
993
994
995
# File 'lib/udat.rb', line 992

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.



977
978
979
980
981
982
983
984
985
# File 'lib/udat.rb', line 977

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

#ordered?Boolean

Returns true, if the order of the contents of this collection is significant for comparisons.

Returns:

  • (Boolean)


967
968
969
# File 'lib/udat.rb', line 967

def ordered?
  self.ordered
end

#popObject

Deletes the last entry and returns its value.



649
650
651
# File 'lib/udat.rb', line 649

def pop
  delete_at(-1)
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). Returns self.



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

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 Udat::Collection and returns self.



951
952
953
954
955
956
957
# File 'lib/udat.rb', line 951

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. Returns self.



701
702
703
704
705
# File 'lib/udat.rb', line 701

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. Returns self.



694
695
696
697
698
# File 'lib/udat.rb', line 694

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

#shiftObject

Deletes the first entry and returns its value.



645
646
647
# File 'lib/udat.rb', line 645

def shift
  delete_at(0)
end

#to_aryObject

Same as Udat::Collection#values.



930
931
932
# File 'lib/udat.rb', line 930

def to_ary
  values
end

#to_hashObject

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



919
920
921
922
923
924
925
926
927
928
# File 'lib/udat.rb', line 919

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 Udat::Collection#ordered = false, but returns self.



987
988
989
990
# File 'lib/udat.rb', line 987

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)


972
973
974
# File 'lib/udat.rb', line 972

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.



731
732
733
734
735
736
737
# File 'lib/udat.rb', line 731

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.



800
801
802
# File 'lib/udat.rb', line 800

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

#value_without_keyObject

Returns the last value without key.



796
797
798
# File 'lib/udat.rb', line 796

def value_without_key
  values_without_key.last
end

#valuesObject Also known as: to_a

Returns an Array containing all values of the collection.



912
913
914
915
916
# File 'lib/udat.rb', line 912

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.



757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
# File 'lib/udat.rb', line 757

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

#values_without_keyObject

Returns an Array of values having no key.



748
749
750
751
752
753
754
755
# File 'lib/udat.rb', line 748

def values_without_key
  synchronize do
    require_index_hashes
    return (
      (@key_indicies[nil] || []).collect { |index| @entries[index][1] }
    )
  end
end