Home | History | Annotate | Download | only in protobuf
      1 # Protocol Buffers - Google's data interchange format
      2 # Copyright 2008 Google Inc.  All rights reserved.
      3 # https://developers.google.com/protocol-buffers/
      4 #
      5 # Redistribution and use in source and binary forms, with or without
      6 # modification, are permitted provided that the following conditions are
      7 # met:
      8 #
      9 #     * Redistributions of source code must retain the above copyright
     10 # notice, this list of conditions and the following disclaimer.
     11 #     * Redistributions in binary form must reproduce the above
     12 # copyright notice, this list of conditions and the following disclaimer
     13 # in the documentation and/or other materials provided with the
     14 # distribution.
     15 #     * Neither the name of Google Inc. nor the names of its
     16 # contributors may be used to endorse or promote products derived from
     17 # this software without specific prior written permission.
     18 #
     19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 require 'forwardable'
     32 
     33 #
     34 # This class makes RepeatedField act (almost-) like a Ruby Array.
     35 # It has convenience methods that extend the core C or Java based
     36 # methods.
     37 #
     38 # This is a best-effort to mirror Array behavior.  Two comments:
     39 #  1) patches always welcome :)
     40 #  2) if performance is an issue, feel free to rewrite the method
     41 #     in jruby and C.  The source code has plenty of examples
     42 #
     43 # KNOWN ISSUES
     44 #   - #[]= doesn't allow less used approaches such as `arr[1, 2] = 'fizz'`
     45 #   - #concat should return the orig array
     46 #   - #push should accept multiple arguments and push them all at the same time
     47 #
     48 module Google
     49   module Protobuf
     50     class RepeatedField
     51       extend Forwardable
     52 
     53       # methods defined in C or Java:
     54       #   +
     55       #   [], at
     56       #   []=
     57       #   concat
     58       #   clear
     59       #   dup, clone
     60       #   each
     61       #   push, <<
     62       #   replace
     63       #   length, size
     64       #   ==
     65       #   to_ary, to_a
     66       #   also all enumerable
     67       #
     68       # NOTE:  using delegators rather than method_missing to make the
     69       #        relationship explicit instead of implicit
     70       def_delegators :to_ary,
     71         :&, :*, :-, :'<=>',
     72         :assoc, :bsearch, :bsearch_index, :combination, :compact, :count,
     73         :cycle, :dig, :drop, :drop_while, :eql?, :fetch, :find_index, :flatten,
     74         :include?, :index, :inspect, :join,
     75         :pack, :permutation, :product, :pretty_print, :pretty_print_cycle,
     76         :rassoc, :repeated_combination, :repeated_permutation, :reverse,
     77         :rindex, :rotate, :sample, :shuffle, :shelljoin, :slice,
     78         :to_s, :transpose, :uniq, :|
     79 
     80 
     81       def first(n=nil)
     82         n ? self[0..n] : self[0]
     83       end
     84 
     85 
     86       def last(n=nil)
     87         n ? self[(self.size-n-1)..-1] : self[-1]
     88       end
     89 
     90 
     91       def pop(n=nil)
     92         if n
     93           results = []
     94           n.times{ results << pop_one }
     95           return results
     96         else
     97           return pop_one
     98         end
     99       end
    100 
    101 
    102       def empty?
    103         self.size == 0
    104       end
    105 
    106       # array aliases into enumerable
    107       alias_method :each_index, :each_with_index
    108       alias_method :slice, :[]
    109       alias_method :values_at, :select
    110       alias_method :map, :collect
    111 
    112 
    113       class << self
    114         def define_array_wrapper_method(method_name)
    115           define_method(method_name) do |*args, &block|
    116             arr = self.to_a
    117             result = arr.send(method_name, *args)
    118             self.replace(arr)
    119             return result if result
    120             return block ? block.call : result
    121           end
    122         end
    123         private :define_array_wrapper_method
    124 
    125 
    126         def define_array_wrapper_with_result_method(method_name)
    127           define_method(method_name) do |*args, &block|
    128             # result can be an Enumerator, Array, or nil
    129             # Enumerator can sometimes be returned if a block is an optional argument and it is not passed in
    130             # nil usually specifies that no change was made
    131             result = self.to_a.send(method_name, *args, &block)
    132             if result
    133               new_arr = result.to_a
    134               self.replace(new_arr)
    135               if result.is_a?(Enumerator)
    136                 # generate a fresh enum; rewinding the exiting one, in Ruby 2.2, will
    137                 # reset the enum with the same length, but all the #next calls will
    138                 # return nil
    139                 result = new_arr.to_enum
    140                 # generate a wrapper enum so any changes which occur by a chained
    141                 # enum can be captured
    142                 ie = ProxyingEnumerator.new(self, result)
    143                 result = ie.to_enum
    144               end
    145             end
    146             result
    147           end
    148         end
    149         private :define_array_wrapper_with_result_method
    150       end
    151 
    152 
    153       %w(delete delete_at delete_if shift slice! unshift).each do |method_name|
    154         define_array_wrapper_method(method_name)
    155       end
    156 
    157 
    158       %w(collect! compact! fill flatten! insert reverse!
    159         rotate! select! shuffle! sort! sort_by! uniq!).each do |method_name|
    160         define_array_wrapper_with_result_method(method_name)
    161       end
    162       alias_method :keep_if, :select!
    163       alias_method :map!, :collect!
    164       alias_method :reject!, :delete_if
    165 
    166 
    167       # propagates changes made by user of enumerator back to the original repeated field.
    168       # This only applies in cases where the calling function which created the enumerator,
    169       # such as #sort!, modifies itself rather than a new array, such as #sort
    170       class ProxyingEnumerator < Struct.new(:repeated_field, :external_enumerator)
    171         def each(*args, &block)
    172           results = []
    173           external_enumerator.each_with_index do |val, i|
    174             result = yield(val)
    175             results << result
    176             #nil means no change occured from yield; usually occurs when #to_a is called
    177             if result
    178               repeated_field[i] = result if result != val
    179             end
    180           end
    181           results
    182         end
    183       end
    184 
    185 
    186     end
    187   end
    188 end
    189