revent 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ package revent {
2
+ import flash.events.Event;
3
+
4
+ public class CallEvent extends Event {
5
+ // The connection has opened, communication is ready.
6
+ public static const CONNECT:String = "CONNECT";
7
+ // The connection has closed, do not do any communication.
8
+ public static const CLOSE:String = "CLOSE";
9
+ public static const IO_ERROR:String = "IO_ERROR";
10
+ public static const SECURITY_ERROR:String = "SECURITY_ERROR";
11
+
12
+ public static const CALL:String = "CALL";
13
+ public static const RESULT:String = "RESULT";
14
+ public static const ERROR:String = "ERROR";
15
+
16
+ public var cmd:Object;
17
+ public var value:Object;
18
+
19
+ public function CallEvent(type:String, cmd:Object, value:Object):void {
20
+ this.cmd = cmd;
21
+ this.value = value;
22
+ super(type);
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,95 @@
1
+ package revent {
2
+ import flash.events.*;
3
+ import flash.net.Socket;
4
+ import flash.net.ObjectEncoding;
5
+
6
+ public class Client extends EventDispatcher {
7
+ private static const TYPE_CALL:int = 0;
8
+ private static const TYPE_RESULT:int = 1;
9
+ private static const TYPE_ERROR:int = 2;
10
+
11
+ private var _socket:Socket;
12
+
13
+ public function Client():void {
14
+ _socket = new Socket();
15
+ _socket.objectEncoding = ObjectEncoding.AMF3;
16
+
17
+ _socket.addEventListener(Event.CONNECT, onConnect);
18
+ _socket.addEventListener(Event.CLOSE, onClose);
19
+ _socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
20
+ _socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
21
+ _socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
22
+ }
23
+
24
+ public function connect(host:String, port:int):void {
25
+ _socket.connect(host, port);
26
+ }
27
+
28
+ public function close():void {
29
+ _socket.close();
30
+ }
31
+
32
+ public function get connected():Boolean {
33
+ return _socket.connected;
34
+ }
35
+
36
+ public function call(cmd:Object, value:Object):void {
37
+ _socket.writeObject([TYPE_CALL, cmd, value]);
38
+ _socket.flush();
39
+ }
40
+
41
+ public function result(cmd:Object, value:Object):void {
42
+ _socket.writeObject([TYPE_RESULT, cmd, value]);
43
+ _socket.flush();
44
+ }
45
+
46
+ public function error(cmd:Object, value:Object):void {
47
+ _socket.writeObject([TYPE_ERROR, cmd, value]);
48
+ _socket.flush();
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+
53
+ private function onConnect(event:Event):void {
54
+ dispatchEvent(new CallEvent(CallEvent.CONNECT, null, null));
55
+ }
56
+
57
+ private function onClose(event:Event):void {
58
+ dispatchEvent(new CallEvent(CallEvent.CLOSE, null, null));
59
+ }
60
+
61
+ private function onIOError(event:IOErrorEvent):void {
62
+ dispatchEvent(new CallEvent(CallEvent.IO_ERROR, null, null));
63
+ }
64
+
65
+ private function onSecurityError(event:SecurityErrorEvent):void {
66
+ dispatchEvent(new CallEvent(CallEvent.SECURITY_ERROR, null, null));
67
+ }
68
+
69
+ private function onData(event:ProgressEvent):void {
70
+ try {
71
+ while (true) {
72
+ var o:Array = _socket.readObject();
73
+ var type:int = o[0];
74
+ var cmd:Object = o[1];
75
+ var value:Object = o[2];
76
+
77
+ var e:CallEvent;
78
+ switch (type) {
79
+ case TYPE_CALL:
80
+ e = new CallEvent(CallEvent.CALL, cmd, value);
81
+ break;
82
+ case TYPE_RESULT:
83
+ e = new CallEvent(CallEvent.RESULT, cmd, value);
84
+ break;
85
+ case TYPE_ERROR:
86
+ e = new CallEvent(CallEvent.ERROR, cmd, value);
87
+ break;
88
+ }
89
+ dispatchEvent(e);
90
+ }
91
+ } catch (e:Error) {
92
+ }
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2007 Aaron Smith (aaron@rubyamf.org)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ There is one exception to the above MIT license. WebORB may not use this code
11
+ base in any of their releases of WebORB for RoR.
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ This is a stripped down version of RubyAMF trunk #1270.
2
+ It only contains AMF serializing/deserializing code.
3
+
4
+ See:
5
+ http://www.rubyamf.org
6
+ http://code.google.com/p/rubyamf
@@ -0,0 +1,11 @@
1
+ dir = File.dirname(__FILE__)
2
+
3
+ # utils must be first
4
+ require "#{dir}/util/string"
5
+ require "#{dir}/util/vo_helper"
6
+
7
+ require "#{dir}/app/fault_object"
8
+
9
+ require 'kconv' # For string.toutf8
10
+ require "#{dir}/io/amf_deserializer"
11
+ require "#{dir}/io/amf_serializer"
@@ -0,0 +1,79 @@
1
+ #This stores supporting configuration classes used in the config file to register class mappings and parameter mappings etc.
2
+
3
+ require "#{File.dirname(__FILE__)}/../exception/rubyamf_exception"
4
+
5
+ module RubyAMF
6
+ module Configuration
7
+ #ClassMappings configuration support class
8
+ class ClassMappings
9
+
10
+ # these NEED to be outside the class << self to work
11
+ @ignore_fields = ['created_at','created_on','updated_at','updated_on']
12
+ @translate_case = false
13
+ @class_mappings_by_ruby_class = {}
14
+ @class_mappings_by_actionscript_class = {}
15
+ @scoped_class_mappings_by_ruby_class = {}
16
+ @attribute_names = {}
17
+ @hash_key_access = :symbol
18
+ @translate_case = false
19
+ @force_active_record_ids = true
20
+ @assume_types = false
21
+ @use_ruby_date_time = false
22
+ @use_array_collection = false
23
+ @check_for_associations = true
24
+
25
+ # Aryk: I cleaned up how the class variables are called here. It doesnt matter if you use class variables or instance variables on the class level. Check out this simple tutorial
26
+ # - http://sporkmonger.com/2007/2/19/instance-variables-class-variables-and-inheritance-in-ruby
27
+
28
+ class << self
29
+ include RubyAMF::Exceptions
30
+
31
+ attr_accessor :ignore_fields, :use_array_collection, :default_mapping_scope, :force_active_record_ids, :attribute_names,
32
+ :use_ruby_date_time, :current_mapping_scope, :check_for_associations, :translate_case, :assume_types, :hash_key_access #the rails parameter mapping type
33
+
34
+ def register(mapping) #register a value object map
35
+ #build out ignore field logic
36
+ hashed_ignores = {}
37
+ ClassMappings.ignore_fields.to_a.each{|k| hashed_ignores[k] = true} # strings and nils will be put into an array with to_a
38
+ mapping[:ignore_fields].to_a.each{|k| hashed_ignores[k] = true}
39
+ mapping[:ignore_fields] = hashed_ignores # overwrite the original ignore fields
40
+
41
+ # if they specify custom attributes, ensure that AR ids are being passed as well if they opt for it.
42
+ if force_active_record_ids && mapping[:attributes] && mapping[:type]=="active_record" && !mapping[:attributes].include?("id")
43
+ mapping[:attributes] << "id"
44
+ end
45
+
46
+ # created caching hashes for mapping
47
+ @class_mappings_by_ruby_class[mapping[:ruby]] = mapping # for quick referencing purposes
48
+ @class_mappings_by_actionscript_class[mapping[:actionscript]] = mapping # for quick referencing purposes
49
+ @scoped_class_mappings_by_ruby_class[mapping[:ruby]] = {} # used later for caching based on scope (will get cached after the first run)
50
+ # for deserialization - looking up in a hash is faster than looking up in an array.
51
+ begin
52
+ if mapping[:type] == "active_record"
53
+ @attribute_names[mapping[:ruby]] = (mapping[:ruby].constantize.new.attribute_names + ["id"]).inject({}){|hash, attr| hash[attr]=true ; hash} # include the id attribute
54
+ end
55
+ rescue ActiveRecord::StatementInvalid => e
56
+ # This error occurs during migrations, since the AR constructed above will check its columns, but the table won't exist yet.
57
+ # We'll ignore the error if we're migrating.
58
+ raise unless ARGV.include?("migrate") or ARGV.include?("db:migrate")
59
+ end
60
+ end
61
+
62
+ def get_vo_mapping_for_ruby_class(ruby_class)
63
+ return unless scoped_class_mapping = @scoped_class_mappings_by_ruby_class[ruby_class] # just in case they didnt specify a ClassMapping for this Ruby Class
64
+ scoped_class_mapping[@current_mapping_scope] ||= (if vo_mapping = @class_mappings_by_ruby_class[ruby_class]
65
+ vo_mapping = vo_mapping.dup # need to duplicate it or else we will overwrite the keys from the original mappings
66
+ vo_mapping[:attributes] = vo_mapping[:attributes][@current_mapping_scope]||[] if vo_mapping[:attributes].is_a?(Hash) # don't include any of these attributes if there is no scope
67
+ vo_mapping[:associations] = vo_mapping[:associations][@current_mapping_scope]||[] if vo_mapping[:associations].is_a?(Hash) # don't include any of these attributes
68
+ vo_mapping
69
+ end
70
+ )
71
+ end
72
+
73
+ def get_vo_mapping_for_actionscript_class(actionscript_class)
74
+ @class_mappings_by_actionscript_class[actionscript_class]
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,13 @@
1
+ #This is a helper to return FaultObjects. Often times there are sitiuations with database logic that requires an "error state"
2
+ #to be set in Flash / Flex, but returning false isn't the best because it still get's mapped to the onResult handler, even returning a
3
+ #generic object with some specific keys set, such as ({error:'someError', code:3}). That is still a pain because it gets mapped to
4
+ #the onResult function still. So return one of these objects to RubyAMF and it will auto generate a faultObject to return to flash
5
+ #so that it maps correctly to the onFault handler.
6
+ class FaultObject < Hash
7
+ def initialize(message = '')
8
+ self['faultCode'] = 1
9
+ self['code'] = 1
10
+ self['message'] = message
11
+ self['faultString'] = message
12
+ end
13
+ end
@@ -0,0 +1,95 @@
1
+ module RubyAMF
2
+ module Exceptions
3
+
4
+ #Encompasses all rubyamf specific exceptions that occur
5
+ class RUBYAMFException < Exception
6
+
7
+ #when version is not 0 or 3
8
+ @VERSION_ERROR = 'RUBYAMF_AMF_VERSION_ERROR'
9
+
10
+ #when translating the target_uri of a body, there isn't a .(period) to map the service / method name
11
+ @SERVICE_TRANSLATION_ERROR = 'RUBYAMF_SERVICE_TRANSLATION_ERROR'
12
+
13
+ #when an authentication error occurs
14
+ @AUTHENTICATION_ERROR = 'RUBYAMF_ATUHENTICATION_ERROR'
15
+
16
+ #when a method is called, but the method is either private or doesn't exist
17
+ @METHOD_ACCESS_ERROR = 'RUBYAMF_METHOD_ACCESS_ERROR'
18
+
19
+ #when a mehod is undefined
20
+ @METHOD_UNDEFINED_METHOD_ERROR = 'RUBYAMF_UNDECLARED_METHOD_ERROR'
21
+
22
+ #when there is an error with session implementation
23
+ @SESSION_ERROR = 'RUBYAMF_SESSION_ERROR'
24
+
25
+ #when a general user error has occured
26
+ @USER_ERROR = 'RUBYAMF_USER_ERROR'
27
+
28
+ #when parsing AMF3, an undefined object reference
29
+ @UNDEFINED_OBJECT_REFERENCE_ERROR = 'RUBYAMF_UNDEFINED_OBJECT_REFERENCE_ERROR'
30
+
31
+ #when parsing AMF3, an undefined class definition
32
+ @UNDEFINED_DEFINITION_REFERENCE_ERROR = 'RUBYAMF_UNDEFINED_DEFINIITON_REFERENCE_ERROR'
33
+
34
+ #when parsing amf3, an undefined string reference
35
+ @UNDEFINED_STRING_REFERENCE_ERROR = 'RUBYAMF_UNDEFINED_STRING_REFERENCE_ERROR'
36
+
37
+ #unsupported AMF0 type
38
+ @UNSUPPORTED_AMF0_TYPE = 'UNSUPPORTED_AMF0_TYPE'
39
+
40
+ #when the Rails ActionController Filter chain haults
41
+ @FILTER_CHAIN_HAULTED = 'RAILS_ACTION_CONTROLLER_FILTER_CHAIN_HAULTED'
42
+
43
+ #when active record errors
44
+ @ACTIVE_RECORD_ERRORS = 'ACTIVE_RECORD_ERRORS'
45
+
46
+ #whan amf data is incomplete or incorrect
47
+ @AMF_ERROR = 'AMF_ERROR'
48
+
49
+ #vo errors
50
+ @VO_ERROR = 'VO_ERROR'
51
+
52
+ #when a parameter mapping error occurs
53
+ @PARAMETER_MAPPING_ERROR = "PARAMETER_MAPPING_ERROR"
54
+
55
+ attr_accessor :message
56
+ attr_accessor :etype
57
+ attr_accessor :ebacktrace
58
+
59
+ #static accessors
60
+ class << self
61
+ attr_accessor :VERSION_ERROR
62
+ attr_accessor :SERVICE_TRANSLATION_ERROR
63
+ attr_accessor :AUTHENTICATION_ERROR
64
+ attr_accessor :METHOD_ACCESS_ERROR
65
+ attr_accessor :METHOD_UNDEFINED_METHOD_ERROR
66
+ attr_accessor :SESSION_ERROR
67
+ attr_accessor :USER_ERROR
68
+ attr_accessor :UNDEFINED_OBJECT_REFERENCE_ERROR
69
+ attr_accessor :UNDEFINED_DEFINITION_REFERENCE_ERROR
70
+ attr_accessor :UNDEFINED_STRING_REFERENCE_ERROR
71
+ attr_accessor :UNSUPPORTED_TYPE
72
+ attr_accessor :ADAPTER_ERROR
73
+ attr_accessor :INTERNAL_ERROR
74
+ attr_accessor :UNSUPPORTED_AMF0_TYPE
75
+ attr_accessor :FILTER_CHAIN_HAULTED
76
+ attr_accessor :ACTIVE_RECORD_ERRORS
77
+ attr_accessor :VO_ERROR
78
+ attr_accessor :AMF_ERROR
79
+ attr_accessor :PARAMETER_MAPPING_ERROR
80
+ end
81
+
82
+ def initialize(type,msg)
83
+ super(msg)
84
+ @message = msg
85
+ @etype = type
86
+ end
87
+
88
+ # stringify the message
89
+ def to_s
90
+ @msg
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,315 @@
1
+ module RubyAMF
2
+ module IO
3
+ class AMFDeserializer
4
+
5
+ require "#{File.dirname(__FILE__)}/read_write"
6
+
7
+ include RubyAMF::Configuration
8
+ include RubyAMF::Exceptions
9
+ include RubyAMF::IO::BinaryReader
10
+ include RubyAMF::IO::Constants
11
+ include RubyAMF::VoHelper
12
+
13
+ attr_accessor :stream
14
+ attr_accessor :stream_position
15
+
16
+ def initialize
17
+ reset
18
+ end
19
+
20
+ def reset
21
+ @stream = ''
22
+ @stream_position = 0
23
+ reset_referencables
24
+ end
25
+
26
+ def reset_referencables
27
+ @stored_strings = []
28
+ @stored_objects = []
29
+ @stored_defs = []
30
+ end
31
+
32
+ def read_amf3
33
+ type = read_word8
34
+ case type
35
+ when AMF3_UNDEFINED
36
+ nil
37
+ when AMF3_NULL
38
+ nil
39
+ when AMF3_FALSE
40
+ false
41
+ when AMF3_TRUE
42
+ true
43
+ when AMF3_INTEGER
44
+ read_amf3_integer
45
+ when AMF3_NUMBER
46
+ read_amf3_number
47
+ when AMF3_STRING
48
+ read_amf3_string
49
+ when AMF3_XML
50
+ read_amf3_xml_string
51
+ when AMF3_DATE
52
+ read_amf3_date
53
+ when AMF3_ARRAY
54
+ read_amf3_array
55
+ when AMF3_OBJECT
56
+ read_amf3_object
57
+ when AMF3_XML_STRING
58
+ read_amf3_xml
59
+ when AMF3_BYTE_ARRAY
60
+ read_amf3_byte_array
61
+ end
62
+ end
63
+
64
+ def read_amf3_integer
65
+ n = 0
66
+ b = read_word8||0
67
+ result = 0
68
+
69
+ while ((b & 0x80) != 0 && n < 3)
70
+ result = result << 7
71
+ result = result | (b & 0x7f)
72
+ b = read_word8||0
73
+ n = n + 1
74
+ end
75
+
76
+ if (n < 3)
77
+ result = result << 7
78
+ result = result | b
79
+ else
80
+ #Use all 8 bits from the 4th byte
81
+ result = result << 8
82
+ result = result | b
83
+
84
+ #Check if the integer should be negative
85
+ if (result > AMF3_INTEGER_MAX)
86
+ result -= (1 << 29)
87
+ end
88
+ end
89
+ return result
90
+ end
91
+
92
+ def read_amf3_string
93
+ type = read_amf3_integer
94
+ isReference = (type & 0x01) == 0
95
+ if isReference
96
+ reference = type >> 1
97
+ if reference < @stored_strings.length
98
+ if @stored_strings[reference] == nil
99
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant string at index #{reference}, please tell aaron@rubyamf.org"))
100
+ end
101
+ return @stored_strings[reference]
102
+ else
103
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_STRING_REFERENCE_ERROR, "Reference to non existant string at index #{reference}, please tell aaron@rubyamf.org") )
104
+ end
105
+ else
106
+
107
+ length = type >> 1
108
+
109
+ #Note that we have to read the string into a byte buffer and then
110
+ #convert to a UTF-8 string, because for standard readUTF() it
111
+ #reads an unsigned short to get the string length.
112
+ #A string isn't stored as a reference if it is the empty string
113
+ #thanks Karl von Randow for this
114
+ if length > 0
115
+ str = String.new(readn(length)) #specifically cast as string, as we're reading verbatim from the stream
116
+ str.toutf8 #convert to utf8
117
+ @stored_strings << str
118
+ end
119
+ return str
120
+ end
121
+ end
122
+
123
+ def read_amf3_xml
124
+ type = read_amf3_integer
125
+ length = type >> 1
126
+ readn(length)
127
+ end
128
+
129
+ def read_amf3_date
130
+ type = read_amf3_integer
131
+ isReference = (type & 0x01) == 0
132
+ if isReference
133
+ reference = type >> 1
134
+ if reference < @stored_objects.length
135
+ if @stored_objects[reference] == nil
136
+
137
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant date at index #{reference}, please tell aaron@rubyamf.org"))
138
+ end
139
+ return @stored_objects[reference]
140
+ else
141
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Undefined date object reference when deserialing AMF3: #{reference}") )
142
+ end
143
+ else
144
+ seconds = read_double.to_f/1000
145
+ time = if (seconds < 0) || ClassMappings.use_ruby_date_time # we can't use Time if its a negative second value
146
+ DateTime.strptime(seconds.to_s, "%s")
147
+ else
148
+ Time.at(seconds)
149
+ end
150
+ @stored_objects << time
151
+ time
152
+ end
153
+ end
154
+
155
+ def read_amf3_array
156
+ type = read_amf3_integer
157
+ isReference = (type & 0x01) == 0
158
+
159
+ if isReference
160
+ reference = type >> 1
161
+ if reference < @stored_objects.length
162
+ if @stored_objects[reference] == nil
163
+ raise(RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant array at index #{reference}, please tell aaron@rubyamf.org"))
164
+ end
165
+ return @stored_objects[reference]
166
+ else
167
+ raise Exception.new("Reference to non-existent array at index #{reference}, please tell aaron@rubyamf.org")
168
+ end
169
+ else
170
+ length = type >> 1
171
+ propertyName = read_amf3_string
172
+ if propertyName != nil
173
+ array = {}
174
+ @stored_objects << array
175
+ begin
176
+ while(propertyName.length)
177
+ value = read_amf3
178
+ array[propertyName] = value
179
+ propertyName = read_amf3_string
180
+ end
181
+ rescue Exception => e #end of object exception, because propertyName.length will be non existent
182
+ end
183
+ 0.upto(length - 1) do |i|
184
+ array["" + i.to_s] = read_amf3
185
+ end
186
+ else
187
+ array = []
188
+ @stored_objects << array
189
+ 0.upto(length - 1) do
190
+ array << read_amf3
191
+ end
192
+ end
193
+ array
194
+ end
195
+ end
196
+
197
+ def read_amf3_object
198
+ type = read_amf3_integer
199
+ isReference = (type & 0x01) == 0
200
+
201
+ if isReference
202
+ reference = type >> 1
203
+ if reference < @stored_objects.length
204
+ if @stored_objects[reference] == nil
205
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant object at index #{reference}, please tell aaron@rubyamf.org."))
206
+ end
207
+ return @stored_objects[reference]
208
+ else
209
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant object #{reference}"))
210
+ end
211
+ else
212
+
213
+ class_type = type >> 1
214
+ class_is_reference = (class_type & 0x01) == 0
215
+
216
+ if class_is_reference
217
+ class_reference = class_type >> 1
218
+ if class_reference < @stored_defs.length
219
+ class_definition = @stored_defs[class_reference]
220
+ else
221
+ raise RUBYAMFException.new(RUBYAMFException.UNDEFINED_DEFINITION_REFERENCE_ERROR, "Reference to non existant class definition #{class_reference}")
222
+ end
223
+ else
224
+ actionscript_class_name = read_amf3_string
225
+ externalizable = (class_type & 0x02) != 0
226
+ dynamic = (class_type & 0x04) != 0
227
+ attribute_count = class_type >> 3
228
+
229
+ class_attributes = []
230
+ attribute_count.times{class_attributes << read_amf3_string} # Read class members
231
+
232
+ class_definition = {"as_class_name" => actionscript_class_name, "members" => class_attributes, "externalizable" => externalizable, "dynamic" => dynamic}
233
+ @stored_defs << class_definition
234
+ end
235
+ action_class_name = class_definition['as_class_name'] #get the className according to type
236
+
237
+ # check to see if its the first main amf object or a flex message obj, because then we need a _explicitType field type and skip some things
238
+ skip_mapping = if action_class_name && action_class_name.include?("flex.messaging")
239
+ obj = VoHash.new # initialize an empty VoHash value holder
240
+ obj._explicitType = action_class_name
241
+ true
242
+ else # otherwise just use a normal hash
243
+ obj = {}
244
+ false
245
+ end
246
+
247
+ obj_position = @stored_objects.size # need to replace the object later for referencing (MUST be before inserting the object into stored_objs)
248
+ @stored_objects << obj
249
+
250
+ if class_definition['externalizable']
251
+ if ['flex.messaging.io.ObjectProxy','flex.messaging.io.ArrayCollection'].include?(action_class_name)
252
+ obj = read_amf3
253
+ else
254
+ raise( RUBYAMFException.new(RUBYAMFException.USER_ERROR, "Unable to read externalizable data type #{type}"))
255
+ end
256
+ else
257
+ translate_case = !skip_mapping&&ClassMappings.translate_case # remove the need for a method call / also, don't want to convert on main remoting object
258
+ class_definition['members'].each do |key|
259
+ value = read_amf3
260
+ #if (value)&& value != 'NaN'# have to read key to move the reader ahead in the stream
261
+ key.to_snake! if translate_case
262
+ obj[key] = value
263
+ #end
264
+ end
265
+
266
+ if class_definition['dynamic']
267
+ while (key = read_amf3_string) && key.length != 0 do # read next key
268
+ value = read_amf3
269
+ #if (value) && value != 'NaN'
270
+ key.to_snake! if translate_case
271
+ obj[key] = value
272
+ #end
273
+ end
274
+ end
275
+ obj = VoUtil.get_vo_for_incoming(obj,action_class_name) unless skip_mapping
276
+ end
277
+ @stored_objects[obj_position] = obj # put the new object into the same position as the original object since it was worked on
278
+ obj
279
+ end
280
+ end
281
+
282
+ def read_amf3_byte_array # according to the charles amf3 deserializer, they store byte array
283
+ type = read_amf3_integer
284
+ isReference = (type & 0x01) == 0
285
+ if isReference
286
+ reference = type >> 1
287
+ if reference < @stored_objects.length
288
+ if @stored_objects[reference] == nil
289
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant byteArray at index #{reference}, please tell aaron@rubyamf.org"))
290
+ end
291
+ return @stored_objects[reference]
292
+ else
293
+ raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant byteArray #{reference}"))
294
+ end
295
+ else
296
+ length = type >> 1
297
+ begin # first assume its gzipped and rescue an exception if its not
298
+ inflated_stream = Zlib::Inflate.inflate( self.stream[self.stream_position,length] )
299
+ arr = inflated_stream.unpack('c'*inflated_stream.length)
300
+ rescue Exception => e
301
+ arr = self.stream[self.stream_position,length].unpack('c'*length)
302
+ end
303
+ self.stream_position += length
304
+ @stored_objects << arr
305
+ arr
306
+ end
307
+ end
308
+
309
+ def read_amf3_number
310
+ res = read_double
311
+ res.is_a?(Float)&&res.nan? ? nil : res # check for NaN and convert them to nil
312
+ end
313
+ end
314
+ end
315
+ end