revent 0.1 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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