Class: Java::ObjectInputStream

Inherits:
Object
  • Object
show all
Includes:
ObjectStream
Defined in:
lib/javaobs.rb

Overview

The Ruby version of the Java ObjectInputStream. Creates a Ruby proxy class for each Java Class.

Constant Summary

Constants included from ObjectStream

Java::ObjectStream::PRIM_ARRAY, Java::ObjectStream::PRIM_BOOL, Java::ObjectStream::PRIM_BYTE, Java::ObjectStream::PRIM_CHAR, Java::ObjectStream::PRIM_DOUBLE, Java::ObjectStream::PRIM_FLOAT, Java::ObjectStream::PRIM_INT, Java::ObjectStream::PRIM_LONG, Java::ObjectStream::PRIM_OBJECT, Java::ObjectStream::PRIM_SHORT, Java::ObjectStream::SC_BLOCKDATA, Java::ObjectStream::SC_EXTERNALIZABLE, Java::ObjectStream::SC_SERIALIZABLE, Java::ObjectStream::SC_WRITE_METHOD, Java::ObjectStream::STREAM_MAGIC, Java::ObjectStream::STREAM_VERSION, Java::ObjectStream::TC_ARRAY, Java::ObjectStream::TC_BLOCKDATA, Java::ObjectStream::TC_BLOCKDATALONG, Java::ObjectStream::TC_CLASS, Java::ObjectStream::TC_CLASSDESC, Java::ObjectStream::TC_ENDBLOCKDATA, Java::ObjectStream::TC_EXCEPTION, Java::ObjectStream::TC_LONGSTRING, Java::ObjectStream::TC_NULL, Java::ObjectStream::TC_OBJECT, Java::ObjectStream::TC_PROXYCLASSDESC, Java::ObjectStream::TC_REFERENCE, Java::ObjectStream::TC_RESET, Java::ObjectStream::TC_STRING

Instance Method Summary collapse

Constructor Details

#initialize(str) ⇒ ObjectInputStream

Initialize from a stream.



477
478
479
480
481
482
483
484
485
486
# File 'lib/javaobs.rb', line 477

def initialize(str)
  @str = str
  magic =  readUShort
  streamVersion = readShort
  @objects = []

  raise "Bad stream #{magic.to_s(16)}:#{streamVersion.to_s(16)}" if magic != STREAM_MAGIC ||
  streamVersion != STREAM_VERSION

end

Instance Method Details

#defaultReadObject(object) ⇒ Object

Cover method for java method.



411
412
413
# File 'lib/javaobs.rb', line 411

def defaultReadObject(object)
  readObjectFields(object.class.javaClass, object)
end

#readArray(klass) ⇒ Object

Read an array of objects.



365
366
367
368
369
370
371
372
373
# File 'lib/javaobs.rb', line 365

def readArray(klass)
  size = readInt
  a = klass.rubyClass.new
  type = klass.arrayType
  1.upto(size) do
    a << readType(type)
  end
  a
end

#readBlockDataObject

Read a Java block of data with a size and then the following data.



262
263
264
265
266
267
# File 'lib/javaobs.rb', line 262

def readBlockData
  size = readBlockStart
  data = @str.read(size)
  readBlockEnd
  data
end

#readBlockEndObject

Raises:



256
257
258
259
# File 'lib/javaobs.rb', line 256

def readBlockEnd
  byte = readByte
  raise SerializationError, "Unexpected byte #{byte}" unless byte == TC_ENDBLOCKDATA
end

#readBlockStartObject



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/javaobs.rb', line 240

def readBlockStart
  byte = readByte
  size = nil
  case byte
  when TC_BLOCKDATA
    size = readByte
    
  when TC_BLOCKDATALONG
    size = readInt
    
  else
    raise SerializationError, "Expecting TC_BLOCKDATA, got #{'0x%X' % byte}" unless byte == TC_BLOCKDATA
  end
  size
end

#readBoolObject



236
# File 'lib/javaobs.rb', line 236

def readBool; @str.read(1)[0] != 0; end

#readByteObject



229
# File 'lib/javaobs.rb', line 229

def readByte; @str.read(1)[0]; end

#readClassAnnotationObject

Read the class annotation. We do not currently handle annotations.

Raises:



287
288
289
290
# File 'lib/javaobs.rb', line 287

def readClassAnnotation
  ebd = readByte
  raise SerializationError, "We do not handle annotations!" unless ebd == TC_ENDBLOCKDATA
end

#readClassData(klass, object = nil) ⇒ Object

Read class data and recursively read parent classes.



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/javaobs.rb', line 424

def readClassData(klass, object = nil)
  if object == nil
    object = klass.rubyClass.new()
    @objects << object
  end
  
  readClassData(klass.superClass, object) if (klass.superClass)
  
  if klass.flags == SC_SERIALIZABLE
    readObjectFields(klass, object)
  else
    object._readJavaData(self)
  end
  
  object
end

#readClassDescObject

Read the Java class description and create a Ruby class to proxy it in this name-space. Added special handling for the Date class.



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/javaobs.rb', line 321

def readClassDesc
  name = readString
  uid = readUID
  flags = readByte
  klass = JavaClass.new(name, uid, flags)
  
  @objects << klass
  
  readFields(klass)
  readClassAnnotation
  
  klass.superClass = readObject

  # Create Ruby object representing class
  if name =~ /^[A-Z.]+/i
    # Create the class within the correct module.
    rclass = rubyClassFor(name, klass.superClass.to_s)

    unless rclass.methods.index('javaClass')
      rclass.class_eval "extend JavaObject"
    end
    
    rclass.class_eval "@javaClass = klass"
    vars = klass.fields.map do |f|
      ':' + f.name
    end
    rclass.class_eval "attr_accessor #{vars.join(',')}"
    klass.rubyClass = rclass
  else
    # Arrays
    newName = 'JavaArray' + klass.name[1..klass.name.length]
    unless Java.constants.index(newName)
      rclass = Java.module_eval "#{newName} = Class.new(JavaArray)"
    else
      rclass = Java.const_get(newName)
    end
    rclass.class_eval "@javaClass = klass"
    klass.rubyClass = rclass
  end
  
  klass
end

#readDoubleObject



233
# File 'lib/javaobs.rb', line 233

def readDouble; @str.read(8).unpack("G")[0]; end

#readFields(klass) ⇒ Object

Read all the fields from the stream and add them to the class.



271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/javaobs.rb', line 271

def readFields(klass)
  fieldCount = readShort
  1.upto(fieldCount) do
    type = readByte
    name = readString
    field = JavaField.new(name, type)
    
    # Check for array and object types
    if type == PRIM_OBJECT || type == PRIM_ARRAY
      field.subtype = readObject
    end
    klass.addField(field)
  end
end

#readFloatObject



234
# File 'lib/javaobs.rb', line 234

def readFloat; @str.read(4).unpack("g")[0]; end

#readIntObject



232
# File 'lib/javaobs.rb', line 232

def readInt; @str.read(4).unpack("i")[0]; end

#readLongObject



238
# File 'lib/javaobs.rb', line 238

def readLong; @str.read(8).unpack("Q").first; end

#readObjectObject

Read an object from the stream.



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/javaobs.rb', line 442

def readObject
  object = nil
  byte = readByte
  case byte
  when TC_OBJECT
    klass = readObject
    object = readClassData(klass)
    
  when TC_REFERENCE
    readShort
    object = @objects[readShort]
    
  when TC_ARRAY
    klass = readObject
    object = readArray(klass)
    @objects << object
    
  when TC_STRING
    object = readString
    @objects << object

  when TC_CLASSDESC
    object = readClassDesc

  when TC_NULL
    object = nil
    
  else
    raise SerializationError, "Unexpected byte #{byte} at #{@str.pos}"
  end

  object
end

#readObjectFields(klass, object) ⇒ Object

Reads the object fields from the stream.



416
417
418
419
420
421
# File 'lib/javaobs.rb', line 416

def readObjectFields(klass, object)
  klass.fields.each do |f|
    v = readType(f.type, f.subtype, f)
    object.send((f.name + '=').intern, v)
  end
end

#readObjectsObject

Read all objects in the stream. Calls readObject until the stream eof is reached.



490
491
492
493
494
495
496
# File 'lib/javaobs.rb', line 490

def readObjects
  objs = []
  until (@str.eof)
    objs << readObject
  end
  objs
end

#readShortObject



231
# File 'lib/javaobs.rb', line 231

def readShort; @str.read(2).unpack("s")[0]; end

#readStringObject



235
# File 'lib/javaobs.rb', line 235

def readString; @str.read(readShort); end

#readType(type, arrayType = nil, field = nil) ⇒ Object

Read a primitive data type.



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/javaobs.rb', line 376

def readType(type, arrayType = nil, field = nil)
  case type
  when PRIM_BYTE
    readByte
    
  when PRIM_CHAR
    readByte
    
  when PRIM_DOUBLE
    readDouble
    
  when PRIM_FLOAT
    readFloat
    
  when PRIM_INT
    readInt
    
  when PRIM_LONG
    readLong
    
  when PRIM_SHORT
    readShort
    
  when PRIM_BOOL
    readBool
    
  when PRIM_OBJECT, PRIM_ARRAY
    readObject
    
  else
    raise SerializationError, "Unknown type #{type}"
  end
end

#readUIDObject



237
# File 'lib/javaobs.rb', line 237

def readUID; @str.read(8); end

#readUShortObject



230
# File 'lib/javaobs.rb', line 230

def readUShort; @str.read(2).unpack("n")[0]; end

#rubyClassFor(name, super_name) ⇒ Object

Gets or creates a corresponding Ruby class for the Java class. This will drop leading com or java and transform the rest of the dotted names to modules.



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/javaobs.rb', line 295

def rubyClassFor(name, super_name)
  context = name.split('.')
  f, = context
  context.shift if f == 'java' or f == 'com'
  context.map! { |n| n.capitalize! if n !~ /^[A-Z]/o; n }
  kname = context.pop
  
  mod = Java
  context.each do |m|
    unless mod.constants.include?(m)
      mod = mod.module_eval "#{m} = Module.new"
    else
      mod = mod.const_get(m)
    end
  end
  
  unless mod.constants.include?(kname)
    rclass = mod.module_eval "#{kname} = Class.new(#{super_name})"
  else
    rclass = mod.const_get(kname)
  end
  rclass
end