2005/5/9

     
 

JavaObjectReader.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_java/src/acdk/java/serialization/JavaObjectReader.cpp,v 1.12 2005/02/05 10:45:11 kommer Exp $

#include "JavaObjectReader.h"
#include "ClassDescription.h"
#include <acdk/locale/UTF8Encoding.h>

/*
stream:
  magic version contents

contents:
  content
  contents content

content:
  object
  blockdata

object:
  newObject
  newClass
  newArray
  newString
  newClassDesc
  prevObject
  nullReference
  exception
  TC_RESET

newClass:
  TC_CLASS classDesc newHandle

classDesc:
  newClassDesc
  nullReference
  (ClassDesc)prevObject      // an object required to be of type
                             // ClassDesc

superClassDesc:
  classDesc

newClassDesc:
  TC_CLASSDESC className serialVersionUID newHandle classDescInfo
  TC_PROXYCLASSDESC newHandle proxyClassDescInfo

classDescInfo:
  classDescFlags fields classAnnotation superClassDesc 

className:
  (utf)

serialVersionUID:
  (long)

classDescFlags:
  (byte)                  // Defined in Terminal Symbols and
                            // Constants

proxyClassDescInfo:
  (int)<count> proxyInterfaceName[count] classAnnotation
      superClassDesc

proxyInterfaceName:
  (utf)

fields:
  (short)<count>  fieldDesc[count]

fieldDesc:
  primitiveDesc
  objectDesc

primitiveDesc:
  prim_typecode fieldName

objectDesc:
  obj_typecode fieldName className1

fieldName:
  (utf)

className1:
  (String)object             // String containing the field's type,
                             // in field descriptor format

classAnnotation:
  endBlockData
  contents endBlockData      // contents written by annotateClass

prim_typecode:
  `B'	// byte
  `C'	// char
  `D'	// double
  `F'	// float
  `I'	// integer
  `J'	// long
  `S'	// short
  `Z'	// boolean

obj_typecode:
  `[`	// array
  `L'	// object

newArray:
  TC_ARRAY classDesc newHandle (int)<size> values[size]

newObject:
  TC_OBJECT classDesc newHandle classdata[]  // data for each class

classdata:
  nowrclass                 // SC_SERIALIZABLE & classDescFlag &&
                            // !(SC_WRITE_METHOD & classDescFlags)
  wrclass objectAnnotation  // SC_SERIALIZABLE & classDescFlag &&
                            // SC_WRITE_METHOD & classDescFlags
  externalContents          // SC_EXTERNALIZABLE & classDescFlag &&
                            // !(SC_BLOCKDATA  & classDescFlags
  objectAnnotation          // SC_EXTERNALIZABLE & classDescFlag&& 
                            // SC_BLOCKDATA & classDescFlags

nowrclass:
  values                    // fields in order of class descriptor

wrclass:
  nowrclass

objectAnnotation:
  endBlockData
  contents endBlockData     // contents written by writeObject
                            // or writeExternal PROTOCOL_VERSION_2.

blockdata:
  blockdatashort
  blockdatalong

blockdatashort:
  TC_BLOCKDATA (unsigned byte)<size> (byte)[size]

blockdatalong:
  TC_BLOCKDATALONG (int)<size> (byte)[size]

endBlockData	:
  TC_ENDBLOCKDATA

externalContent:          // Only parseable by readExternal
  ( bytes)                // primitive data
    object

externalContents:         // externalContent written by 
  externalContent         // writeExternal in PROTOCOL_VERSION_1.
  externalContents externalContent

newString:
  TC_STRING newHandle (utf)
  TC_LONGSTRING newHandle (long-utf)

prevObject
  TC_REFERENCE (int)handle

nullReference
  TC_NULL

exception:
  TC_EXCEPTION reset (Throwable)object	 reset 

magic:
  STREAM_MAGIC

version
  STREAM_VERSION

values:          // The size and types are described by the
                 // classDesc for the current object

newHandle:       // The next number in sequence is assigned
                 // to the object being serialized or deserialized

reset:           // The set of known objects is discarded
                 // so the objects of the exception do not
                 // overlap with the previously sent objects 
                 // or with objects that may be sent after 
                 // the exception


  */
namespace acdk {
namespace java {
namespace serialization {

using namespace acdk::lang::dmi;

JavaObjectReader::JavaObjectReader(IN(::acdk::io::RReader) in)
: BinaryObjectReader(in)
, _prevObjects(new JavaObjectReadWriteCache())
{
  _readStreamHeader();
}




void 
JavaObjectReader::_readStreamHeader()
{
  short magic = readShort();
  if (magic != STREAM_MAGIC)
    return;
  short version = readShort();
  if (version != 5)
    return;

}

RString 
JavaObjectReader::readUtf()
{
  int len = readShort(); // is byte len
  acdk::locale::UTF8Decoder dec;
  StringBuffer sb;
  while (dec.bytesReaded() < len)
    sb.append((ucchar)dec.decodeToChar(this));
  return sb.toString();
  /*bytesReaded()
  return dec.decodeToString(this, len);
  */
  /*
  
  RbyteArray ba = new byteArray(len);
  BinaryObjectReader::read(ba);
  return 
  return new String(ba);
  */
}

RString 
JavaObjectReader::readLongUtf()
{
  int len = readInt();
  acdk::locale::UTF8Decoder dec;
   StringBuffer sb;
  while (dec.bytesReaded() < len)
    sb.append((ucchar)dec.decodeToChar(this));
  return sb.toString();

  //return dec.decodeToString(this, len);
  /*
  RbyteArray ba = new byteArray(len);
  BinaryObjectReader::read(ba);
  return new String(ba);
  */
}

RString 
JavaObjectReader::readString()
{
  int tp = read();
  if (tp == -1)
    return Nil;
  if (tp != TC_STRING && tp != TC_LONGSTRING)
    THROW1_FQ(::acdk::io::, ObjectStreamException, "Expecting a String in stream");
  RString str;
  if (tp == TC_STRING)
  {
    str = readUtf();
  } else {
    str = readLongUtf();
  }
  registerNewObject(&str);
  return str;
}

RbyteArray 
JavaObjectReader::readBlock()
{
  int tc = read();
  if (tc == -1)
    return Nil;
  if (tc == TC_ENDBLOCKDATA)
    return Nil;

  RbyteArray ret;
  int size = 0;
  if (tc == TC_BLOCKDATA)
    size = readChar();
  else if (tc == TC_ENDBLOCKDATA)
    size = readInt();
  ret = new byteArray(size);
  read(ret);
  return ret;
}

RClassDescription 
JavaObjectReader::readClassDesc()
{
  int tc = read();
  if (tc == -1)
    return Nil;
  if (tc == TC_NULL)
    return Nil;
  if (tc == TC_REFERENCE)
    return (RClassDescription)_prevObjects->get(readInt());

  RClassDescription cd = ClassDescription::read(this);
  return cd;
}


acdk::lang::Object 
JavaObjectReader::createObject(IN(RString) javaclassname, const ClassTypeMapping* ctm)
{
  RString cname = javaclassname;
  if (ctm != 0)
  {
    cname = ctm->acdk_name;
  } else {
    if (cname->startsWith("java/") == true)
      cname = "acdk/" + cname->substr(strlen("java/"));
  }
  RClass cls = Class::forName(cname);
  if (cls == Nil)
    return Nil;
  return cls->newInstance();
}



#define READ_ARRAY(size, arraytype, readType) \
do { \
  R##arraytype##Array array = new arraytype##Array(size); \
  registerNewObject(&array); \
  for (int i = 0; i < size; ++i) \
    array[i] = read##readType(); \
  return (acdk::lang::Object)array; \
} while (false) 

//foreign virtual 
acdk::lang::Object 
JavaObjectReader::readObject()
{
  int tp = read();
  if (tp == -1)
    return Nil;
  if (tp == TC_NULL)
    return Nil;
  if (tp == TC_REFERENCE)
  {
    return _prevObjects->get(readInt());
  }
  acdk::lang::Object obj = readObject2(tp);
  return obj;
}

//foreign virtual 
acdk::lang::Object 
JavaObjectReader::readObject2(int tp)
{
  if (tp == TC_STRING || tp == TC_LONGSTRING)
  {
    RString str;
    if (tp == TC_STRING)
    {
      str = readUtf();
    } else {
      str = readLongUtf();
    }
    registerNewObject(&str);
    return &str;
  } 

  if (tp == TC_OBJECT)
  {
    RClassDescription clsdesc = readClassDesc();
    
    const ClassTypeMapping* ctm = ClassTypeMapping::findJavaClass(clsdesc->className());
    if (clsdesc->flags() & SC_SERIALIZABLE)
    {
      if (ctm->read_func != 0)
      {
        acdk::lang::Object obj = ctm->read_func(this, ctm);
        return obj;
      }
      acdk::lang::Object obj = createObject(clsdesc->className(), ctm);
      registerNewObject(obj); 
      for (int i = 0; i < clsdesc->fields()->length(); ++i)
      {
        RFieldDescription fd = clsdesc->fields()[i];
        RString fieldname = fd->fieldName();
        if (ctm != 0)
        {
          const MemberTypeMapping* mtm = ctm->findJavaMember(fd->_fieldName->c_str());
          if (mtm != 0)
          {
            fieldname = mtm->acdk_field;
          }
        }
        SysField  field = obj->getInternalField(fieldname, MiNonStatic); //SysField::getField(fi, ScriptVar());
        switch (fd->typeCode())
        {
        case 'B': 
          field.set(read());
          break;
        case 'C':
          field.set(read());
          break;
        case 'U':
          field.set(readShort());
          break;
        case 'D': 
          field.set(readDouble());
          break;
        case 'F': 
          field.set(readFloat());
          break;
        case 'I':
          field.set(readInt());
          break;
        case 'J':
          field.set(readLong());
          break;
        case 'S':
          field.set(readShort());
          break;
        case 'Z':
          field.set(readBoolean());
          break;
        case '[':
          field.set(readObject());
          break;
        case 'L':
          field.set(readObject());
          break;
        }
      }
      return obj;
    }
 
  } else if (tp == TC_ARRAY) {
    RClassDescription clsdesc = readClassDesc();
    int arraySize = readInt();
    switch (clsdesc->className()->charAt(1))
    {
    case 'B': 
      READ_ARRAY(arraySize, byte, Char);
    case 'C':
    case 'U':
    {
      RuccharArray array = new uccharArray(arraySize);
      registerNewObject(&array); 
      for (int i = 0; i < arraySize; ++i) 
      {
        short s = readShort();
        array[i] = (ucchar)s;
      }
      return &array;
    }/*
    case 'C':
    {
      RcharArray array = new charArray(arraySize);
      registerNewObject(&array); 
      for (int i = 0; i < arraySize; ++i) 
      {
        short s = readShort();
        if (s > 255)
        {
          //####
        }
        array[i] = (char)s;
      }
      return &array;
    }*/
    case 'D': 
      READ_ARRAY(arraySize, double, Double);
    case 'F': 
      READ_ARRAY(arraySize, float, Float);
    case 'I':
      READ_ARRAY(arraySize, int, Int);
    case 'J':
      READ_ARRAY(arraySize, long, Long);
    case 'S':
      READ_ARRAY(arraySize, short, Short);
    case 'Z':
      READ_ARRAY(arraySize, bool, Boolean);
    case '[':
      THROW1_FQ(::acdk::io::, ObjectStreamException, "Arrays from Arrays not supported yet: " + clsdesc->className());
      // ### not supported field.set(readObject());
      break;
    case 'L':
    {
      RString cname = clsdesc->className();
      cname = cname->substr(2, cname->length() - 1);
      
      const ClassTypeMapping* ctm = ClassTypeMapping::findJavaClass(cname);
      if (ctm == 0)
        THROW1_FQ(::acdk::io::, ObjectStreamException, "No Typemapping found for java: " + cname);
      acdk::lang::Object oa = Class::create_arrayInstance(Class::forName(ctm->acdk_name), arraySize);
      registerNewObject(oa); 
      acdk::lang::dmi::SysFields fields = oa->getInternalFields(0);

      //RObjectArray oa = new ObjectArray(arraySize);
      for (int i = 0; i < arraySize; ++i)
      {
        fields[i + 1].set(readObject());
      }
      return oa;
    }
    }
    
  } else if (tp == TC_BLOCKDATA || tp == TC_BLOCKDATALONG) {
    int size = 0;
    if (tp == TC_BLOCKDATA)
      size = (byte)read();
    else
      size = readInt();
    RbyteArray ba = new byteArray(size);
    read(ba); // ignore this 
    return readObject();
    //return ba;
    //THROW1_FQ(::acdk::io::, ObjectStreamException, RString("TC_BLOCKDATA not supported"));
  }
  THROW1_FQ(::acdk::io::, ObjectStreamException, RString("Unknown Stream element type: ") + tp);
  return Nil;
}

} // namespace serialization
} // namespace java 
} // namespace acdk