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.



483
484
485
486
487
488
489
490
491
492
# File 'lib/javaobs.rb', line 483

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.



414
415
416
# File 'lib/javaobs.rb', line 414

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

#readArray(klass) ⇒ Object

Read an array of objects.



368
369
370
371
372
373
374
375
376
# File 'lib/javaobs.rb', line 368

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.



265
266
267
268
269
270
# File 'lib/javaobs.rb', line 265

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

#readBlockEndObject

Read the block end tag. Validate it is correct or raise a SerializationError.

Raises:



259
260
261
262
# File 'lib/javaobs.rb', line 259

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

#readBlockStartObject

Read the block data beginning tag and return the size. We can have a long or short block of data.



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

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:



290
291
292
293
# File 'lib/javaobs.rb', line 290

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. If the class is externalizable then we call the _readJavaData method to ‘ perform the custom serialization. See Java::Util::Hash and Java::Util::Date for an example.



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/javaobs.rb', line 430

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.



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
363
364
365
# File 'lib/javaobs.rb', line 324

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.



274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/javaobs.rb', line 274

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.



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
475
476
477
478
479
480
# File 'lib/javaobs.rb', line 448

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.



419
420
421
422
423
424
# File 'lib/javaobs.rb', line 419

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.



496
497
498
499
500
501
502
# File 'lib/javaobs.rb', line 496

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.



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
409
410
411
# File 'lib/javaobs.rb', line 379

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.



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

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