1#
2# Licensed to the Apache Software Foundation (ASF) under one
3# or more contributor license agreements. See the NOTICE file
4# distributed with this work for additional information
5# regarding copyright ownership. The ASF licenses this file
6# to you under the Apache License, Version 2.0 (the
7# "License"); you may not use this file except in compliance
8# with the License. You may obtain a copy of the License at
9#
10#   http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing,
13# software distributed under the License is distributed on an
14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15# KIND, either express or implied. See the License for the
16# specific language governing permissions and limitations
17# under the License.
18#
19
20# this require is to make generated struct definitions happy
21require 'set'
22
23module Thrift
24  class ProtocolException < Exception
25
26    UNKNOWN = 0
27    INVALID_DATA = 1
28    NEGATIVE_SIZE = 2
29    SIZE_LIMIT = 3
30    BAD_VERSION = 4
31    NOT_IMPLEMENTED = 5
32    DEPTH_LIMIT = 6
33
34    attr_reader :type
35
36    def initialize(type=UNKNOWN, message=nil)
37      super(message)
38      @type = type
39    end
40  end
41
42  class BaseProtocol
43
44    attr_reader :trans
45
46    def initialize(trans)
47      @trans = trans
48    end
49
50    def native?
51      puts "wrong method is being called!"
52      false
53    end
54
55    def write_message_begin(name, type, seqid)
56      raise NotImplementedError
57    end
58
59    def write_message_end; nil; end
60
61    def write_struct_begin(name)
62      raise NotImplementedError
63    end
64
65    def write_struct_end; nil; end
66
67    def write_field_begin(name, type, id)
68      raise NotImplementedError
69    end
70
71    def write_field_end; nil; end
72
73    def write_field_stop
74      raise NotImplementedError
75    end
76
77    def write_map_begin(ktype, vtype, size)
78      raise NotImplementedError
79    end
80
81    def write_map_end; nil; end
82
83    def write_list_begin(etype, size)
84      raise NotImplementedError
85    end
86
87    def write_list_end; nil; end
88
89    def write_set_begin(etype, size)
90      raise NotImplementedError
91    end
92
93    def write_set_end; nil; end
94
95    def write_bool(bool)
96      raise NotImplementedError
97    end
98
99    def write_byte(byte)
100      raise NotImplementedError
101    end
102
103    def write_i16(i16)
104      raise NotImplementedError
105    end
106
107    def write_i32(i32)
108      raise NotImplementedError
109    end
110
111    def write_i64(i64)
112      raise NotImplementedError
113    end
114
115    def write_double(dub)
116      raise NotImplementedError
117    end
118
119    # Writes a Thrift String. In Ruby 1.9+, the String passed will be transcoded to UTF-8.
120    #
121    # str - The String to write.
122    #
123    # Raises EncodingError if the transcoding to UTF-8 fails.
124    #
125    # Returns nothing.
126    def write_string(str)
127      raise NotImplementedError
128    end
129
130    # Writes a Thrift Binary (Thrift String with no encoding). In Ruby 1.9+, the String passed
131    # will forced into BINARY encoding.
132    #
133    # buf - The String to write.
134    #
135    # Returns nothing.
136    def write_binary(buf)
137      raise NotImplementedError
138    end
139
140    def read_message_begin
141      raise NotImplementedError
142    end
143
144    def read_message_end; nil; end
145
146    def read_struct_begin
147      raise NotImplementedError
148    end
149
150    def read_struct_end; nil; end
151
152    def read_field_begin
153      raise NotImplementedError
154    end
155
156    def read_field_end; nil; end
157
158    def read_map_begin
159      raise NotImplementedError
160    end
161
162    def read_map_end; nil; end
163
164    def read_list_begin
165      raise NotImplementedError
166    end
167
168    def read_list_end; nil; end
169
170    def read_set_begin
171      raise NotImplementedError
172    end
173
174    def read_set_end; nil; end
175
176    def read_bool
177      raise NotImplementedError
178    end
179
180    def read_byte
181      raise NotImplementedError
182    end
183
184    def read_i16
185      raise NotImplementedError
186    end
187
188    def read_i32
189      raise NotImplementedError
190    end
191
192    def read_i64
193      raise NotImplementedError
194    end
195
196    def read_double
197      raise NotImplementedError
198    end
199
200    # Reads a Thrift String. In Ruby 1.9+, all Strings will be returned with an Encoding of UTF-8.
201    #
202    # Returns a String.
203    def read_string
204      raise NotImplementedError
205    end
206
207    # Reads a Thrift Binary (Thrift String without encoding). In Ruby 1.9+, all Strings will be returned
208    # with an Encoding of BINARY.
209    #
210    # Returns a String.
211    def read_binary
212      raise NotImplementedError
213    end
214
215    # Writes a field based on the field information, field ID and value.
216    #
217    # field_info - A Hash containing the definition of the field:
218    #              :name   - The name of the field.
219    #              :type   - The type of the field, which must be a Thrift::Types constant.
220    #              :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
221    # fid        - The ID of the field.
222    # value      - The field's value to write; object type varies based on :type.
223    #
224    # Returns nothing.
225    def write_field(*args)
226      if args.size == 3
227        # handles the documented method signature - write_field(field_info, fid, value)
228        field_info = args[0]
229        fid = args[1]
230        value = args[2]
231      elsif args.size == 4
232        # handles the deprecated method signature - write_field(name, type, fid, value)
233        field_info = {:name => args[0], :type => args[1]}
234        fid = args[2]
235        value = args[3]
236      else
237        raise ArgumentError, "wrong number of arguments (#{args.size} for 3)"
238      end
239
240      write_field_begin(field_info[:name], field_info[:type], fid)
241      write_type(field_info, value)
242      write_field_end
243    end
244
245    # Writes a field value based on the field information.
246    #
247    # field_info - A Hash containing the definition of the field:
248    #              :type   - The Thrift::Types constant that determines how the value is written.
249    #              :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
250    # value      - The field's value to write; object type varies based on field_info[:type].
251    #
252    # Returns nothing.
253    def write_type(field_info, value)
254      # if field_info is a Fixnum, assume it is a Thrift::Types constant
255      # convert it into a field_info Hash for backwards compatibility
256      if field_info.is_a? Fixnum
257        field_info = {:type => field_info}
258      end
259
260      case field_info[:type]
261      when Types::BOOL
262        write_bool(value)
263      when Types::BYTE
264        write_byte(value)
265      when Types::DOUBLE
266        write_double(value)
267      when Types::I16
268        write_i16(value)
269      when Types::I32
270        write_i32(value)
271      when Types::I64
272        write_i64(value)
273      when Types::STRING
274        if field_info[:binary]
275          write_binary(value)
276        else
277          write_string(value)
278        end
279      when Types::STRUCT
280        value.write(self)
281      else
282        raise NotImplementedError
283      end
284    end
285
286    # Reads a field value based on the field information.
287    #
288    # field_info - A Hash containing the pertinent data to write:
289    #              :type   - The Thrift::Types constant that determines how the value is written.
290    #              :binary - A flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
291    #
292    # Returns the value read; object type varies based on field_info[:type].
293    def read_type(field_info)
294      # if field_info is a Fixnum, assume it is a Thrift::Types constant
295      # convert it into a field_info Hash for backwards compatibility
296      if field_info.is_a? Fixnum
297        field_info = {:type => field_info}
298      end
299
300      case field_info[:type]
301      when Types::BOOL
302        read_bool
303      when Types::BYTE
304        read_byte
305      when Types::DOUBLE
306        read_double
307      when Types::I16
308        read_i16
309      when Types::I32
310        read_i32
311      when Types::I64
312        read_i64
313      when Types::STRING
314        if field_info[:binary]
315          read_binary
316        else
317          read_string
318        end
319      else
320        raise NotImplementedError
321      end
322    end
323
324    def skip(type)
325      case type
326      when Types::BOOL
327        read_bool
328      when Types::BYTE
329        read_byte
330      when Types::I16
331        read_i16
332      when Types::I32
333        read_i32
334      when Types::I64
335        read_i64
336      when Types::DOUBLE
337        read_double
338      when Types::STRING
339        read_string
340      when Types::STRUCT
341        read_struct_begin
342        while true
343          name, type, id = read_field_begin
344          break if type == Types::STOP
345          skip(type)
346          read_field_end
347        end
348        read_struct_end
349      when Types::MAP
350        ktype, vtype, size = read_map_begin
351        size.times do
352          skip(ktype)
353          skip(vtype)
354        end
355        read_map_end
356      when Types::SET
357        etype, size = read_set_begin
358        size.times do
359          skip(etype)
360        end
361        read_set_end
362      when Types::LIST
363        etype, size = read_list_begin
364        size.times do
365          skip(etype)
366        end
367        read_list_end
368      else
369        raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid data')
370      end
371    end
372
373    def to_s
374      "#{trans.to_s}"
375    end
376  end
377
378  class BaseProtocolFactory
379    def get_protocol(trans)
380      raise NotImplementedError
381    end
382
383    def to_s
384      "base"
385    end
386  end
387end
388