2005/5/9

     
 

AbstractObjectWriter.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_core/src/acdk/io/AbstractObjectWriter.cpp,v 1.30 2005/03/14 15:06:15 kommer Exp $

#include <acdk.h>
#include <acdk/lang/reflect/Modifier.h>
#include "AbstractObjectWriter.h"
#include "ObjectStreamException.h"
#include <acdk/lang/System.h>

#include <acdk/io/PrintWriter.h>
#include <acdk/io/FileWriter.h>
#include <acdk/io/ObjectStreamException.h>

namespace acdk {
namespace io {

//#define LOCAL_DEBUG 
#ifdef LOCAL_DEBUG
RPrintWriter getDout()
{
    //static RPrintWriter _dout = new PrintWriter(new FileWriter("./AbstractObjectWriter.dout"));
    //return _dout;
  return System::out;
}

#define DOUT(strexpr) \
do { \
  StringBuffer sb; \
  for (int _i = 0; _i < identlevel; ++_i) \
    sb.append(" "); \
  sb << strexpr << "\n"; \
  getDout()->print(sb.toString()); \
} while (false)

#else 
# define DOUT(str)
#endif
/FONT>

namespace {
  int identlevel = 0;
  struct Incrementor { Incrementor() { ++identlevel; } ~Incrementor() { --identlevel; }  };
}

#define DOUTLEVEL() Incrementor _inlevel;


//virtual 
void AbstractObjectWriter::writeObject(IN(acdk::lang::Object) obj) 
{ 
  try {
    writeObject2(obj);
    resetLocalRefs();
  } catch (...) {
    throw;
  }
}

//virtual 
void AbstractObjectWriter::writeObject2(IN(acdk::lang::Object) obj) 
{ 
  if (obj == Nil)
    writeObject2(Nil, Nil);
  else
    writeObject2(obj->getClass(), obj);
}

void 
AbstractObjectWriter::writeObject(IN(RClass) cls, IN(acdk::lang::Object) obj)
{
  try {
    writeObject2(cls, obj);
    resetLocalRefs();
  } catch (...) {
    resetLocalRefs();    
    throw;
  }
}

//virtual 
void 
AbstractObjectWriter::writeObject2(IN(RClass) cls, IN(acdk::lang::Object) obj)
{
  if (isTagged() == true) 
    writeTagStart("acdk::lang::Object");
  _writeObject(cls, obj);
  if (isTagged() == true)  
    writeTagEnd("acdk::lang::Object");
   
}

int 
AbstractObjectWriter::_lookupStringRef(IN(RString) str)
{
  for (int i = 0; i < _lrefs.length(); i++)  
  {
    acdk::lang::Object obj = _lrefs[i];
    if (instanceof(obj, String) == true)
    {
      if (RString(obj)->equals(str) == true)
        return i;
    }
  }
  return -1;
}


bool checkSerializable(IN(RClass) cls, INOUT(acdk::lang::Object) obj, int _serializeFlags)
{
  if (SerializeAll & _serializeFlags)
    return true;
  bool isSerializable = cls->isSerializable();
  
  bool returnVal = true;
  if (SerializeOnlySerializable & _serializeFlags && isSerializable == false)
  {
    returnVal = false;
  }
  
  if (returnVal == false && (SerializeIgnoreNotSerializable & _serializeFlags) != 0)
  {
    obj = Nil;
    return true;
  }
  return returnVal;
}

//virtual 
void 
AbstractObjectWriter::writeClassDescriptor(IN(RClass) cls, IN(acdk::lang::Object) obj)
{
  if (isNamed() == true)
    writeClassId(cls);
  // write here serializedVersion if wantet
  // write here field names if requested
}

void 
AbstractObjectWriter::defaultWriteObject(IN(RClass) cls, IN(acdk::lang::Object) obj)
{
  //doen't, because reader doesn't expect meta info write_string(theClass->getName());
  ::acdk::lang::dmi::SysFields members = obj->getInternalFields(::acdk::lang::dmi::MiNonStatic | ::acdk::lang::dmi::MiIvDeclared, cls->objectClazzInfo());
  int membersize = members.size();
  DOUT("WO: " << cls->toString() << " membersize=" << membersize);

  for (int i = 0; i < members.size(); i++) 
  {
    ::acdk::lang::dmi::SysField& f = members[i];
    
    if (::acdk::lang::dmi::MetaInfo::isStatic(f.fieldInfo->flags) == true ||
        f.fieldInfo->isTransient() == true )
      continue;
    DOUTLEVEL();
    DOUT(RString(f.fieldInfo->name) << " type: " << f.type);
    switch (f.type) 
    {
    case ::acdk::lang::dmi::SysField::FT_Void: break; // nothing
    case ::acdk::lang::dmi::SysField::FT_Bool: writeBooleanElement(*f.cont.bval); break;
    case ::acdk::lang::dmi::SysField::FT_Char : writeCharElement(*f.cont.cval);  break;
    case ::acdk::lang::dmi::SysField::FT_UcChar : writeUcCharElement(*f.cont.ucval);  break;
    case ::acdk::lang::dmi::SysField::FT_Byte : writeCharElement(*f.cont.cval);  break;
    case ::acdk::lang::dmi::SysField::FT_Short : writeShortElement(*f.cont.sval);  break;
    case ::acdk::lang::dmi::SysField::FT_Int : writeIntElement((int)*f.cont.ival);  break;
    case ::acdk::lang::dmi::SysField::FT_JLong : writeLongElement(*f.cont.jlval);  break;
    case ::acdk::lang::dmi::SysField::FT_Float : writeFloatElement(*f.cont.fval);  break;
    case ::acdk::lang::dmi::SysField::FT_Double : writeDoubleElement(*f.cont.dval);  break;
    case ::acdk::lang::dmi::SysField::FT_Object : 
    {
      acdk::lang::Object cobj = f.cont.oval->impl();
      writeObject2(cobj);
      break;
    }
    }
  }
}

void 
AbstractObjectWriter::writeObjectHierarchy(IN(RClass) cls, IN(acdk::lang::Object) obj)
{
  RClassArray supers = cls->getSuperClasses();
  for (int i = 1; i < supers->length(); ++i)
  {
    RClass cclass = supers[i];
    if (cclass->hasWriteObject() == true) 
    {
      obj->writeObject(this, cclass);
    }
    else
    {
      defaultWriteObject(cclass, obj);
    }
  }
}

void 
AbstractObjectWriter::writeUnshared(IN(RClass) cls, IN(acdk::lang::Object) obj)
{
  if (isReduced() == true)
  {
     _lrefs.append(obj);
    writeObjectLocalId(_lrefs.length() - 1);
  }
  writeClassDescriptor(cls, obj);
   
  int nid = _lrefs.length() - 1;
  DOUT("WO: " <<  cls->toString() << " lref=" << nid);
  acdk::lang::Object object = obj;
  if (cls->hasWriteReplace() == true)
    object = object->writeReplace();

  if (instanceof(object, String) == true) 
  {
    writeString((RString)object);
    return;
  }
  if (instanceof(object, byteArray) == true)
  {
    writeOpaque(RbyteArray(object));
    return;
  }
  writeObjectHierarchy(cls, object);
  /*
  RClass curCls = cls;

  defaultWriteObject(curCls, obj);
  
    
  if (cls->hasWriteObject() == true) {
    obj->writeObject(this, cls);
    return;
  }
  if (instanceof(obj, Throwable) == true)
    RThrowable(obj)->getStackFrames();

  //doen't, because reader doesn't expect meta info write_string(theClass->getName());
  ::acdk::lang::dmi::SysFields members = obj->getInternalFields(::acdk::lang::dmi::MiNonStatic);
  int membersize = members.size();
  DOUT("WO: " << cls->toString() << " membersize=" << membersize);

  for (int i = 0; i < members.size(); i++) 
  {
    ::acdk::lang::dmi::SysField& f = members[i];
    
    if (::acdk::lang::dmi::MetaInfo::isStatic(f.fieldInfo->flags) == true ||
        f.fieldInfo->isTransient() == true )
      continue;
    DOUTLEVEL();
    DOUT(RString(f.fieldInfo->name) << " type: " << f.type);
    switch (f.type) 
    {
    case ::acdk::lang::dmi::SysField::FT_Void: break; // nothing
    case ::acdk::lang::dmi::SysField::FT_Bool: writeBooleanElement(*f.cont.bval); break;
    case ::acdk::lang::dmi::SysField::FT_Char : writeCharElement(*f.cont.cval);  break;
    case ::acdk::lang::dmi::SysField::FT_UcChar : writeUcCharElement(*f.cont.ucval);  break;
    case ::acdk::lang::dmi::SysField::FT_Byte : writeCharElement(*f.cont.cval);  break;
    case ::acdk::lang::dmi::SysField::FT_Short : writeShortElement(*f.cont.sval);  break;
    case ::acdk::lang::dmi::SysField::FT_Int : writeIntElement((int)*f.cont.ival);  break;
    case ::acdk::lang::dmi::SysField::FT_JLong : writeLongElement(*f.cont.jlval);  break;
    case ::acdk::lang::dmi::SysField::FT_Float : writeFloatElement(*f.cont.fval);  break;
    case ::acdk::lang::dmi::SysField::FT_Double : writeDoubleElement(*f.cont.dval);  break;
    case ::acdk::lang::dmi::SysField::FT_Object : 
    {
      acdk::lang::Object cobj = f.cont.oval->impl();
      writeObject2(cobj);
      break;
    }
    }
  }
  */
}

void
AbstractObjectWriter::_writeObject(IN(RClass) cls, IN(acdk::lang::Object) tobj)
{
  acdk::lang::Object object = tobj;
  if (cls != Nil)
  {
    acdk::lang::Object elementobject = object;

    RClass elclass = cls;
    while (elclass->isArray() == true)
    {
      elclass = elclass->getArrayElementClass();
    }
    if (elclass != String::GetClass())
    {
      if (checkSerializable(elclass, object, _serializeFlags) == false)
        THROW1(NotSerializableException, "Class " + cls->getName() + " is not serializable");
      
    }
  }

  if (isReduced() == true) 
  {
    for (int i = 0; i < _lrefs.length(); i++)  
    {
      if (object == _lrefs[i]) {
        writeObjectLocalId(i);
        DOUT("WRO: " << (object == Nil ? RString("<Nil>") : object->getClass()->toString()) << " lref=" << Integer::toString(i));
        return;
      }
    }
  }
  
  if (object == Nil) {
    if (isNamed() == false)
      THROW1(StreamCorruptedException, "Cannot write Nil acdk::lang::Object in untagged stream");
    DOUT("WO: <NilObject> lref=" << _lrefs.length() - 1);
    _lrefs.append(object);
    writeObjectLocalId(_lrefs.length() - 1);
    writeClassId(Nil);
    return;
  }
  writeUnshared(cls, object);

  
 
}

using ::acdk::lang::dmi::ScriptVar;

//virtual 
void 
AbstractObjectWriter::writeScriptVar(ScriptVar& arg, bool withTypeInfo, bool withFlags)
{
  if (isTagged() == true)  
    writeTagStart("ScriptVar");
  
  if (withTypeInfo == true)
    writeInt(arg.type);
  if (withFlags == true)
    writeInt(arg.flags);
  
  DOUT("SC: type=" << arg.type << ": " << arg.toString());
  DOUTLEVEL();

  switch (arg.type) {
  case ScriptVar::BoolType : writeBoolean(arg.getBoolVar()); break;
  case ScriptVar::CharType : writeChar(arg.getCharVar()); break;
  case ScriptVar::ByteType : writeChar(arg.getByteVar()); break;
  case ScriptVar::ShortType : writeShort(arg.getShortVar()); break;
  case ScriptVar::IntType : writeInt(arg.getIntVar()); break;
  case ScriptVar::LongType : writeLong(arg.getLongVar()); break;
  case ScriptVar::FloatType : writeFloat(arg.getFloatVar()); break;
  case ScriptVar::DoubleType : writeDouble(arg.getDoubleVar()); break;
  case ScriptVar::ObjectType : writeObject2(arg.getObjectVar()); break;
  case ScriptVar::UnknownType: 
  default :
    break;
  }
  if (isTagged() == true)  
    writeTagEnd("ScriptVar");
}

//foreign  virtual 
void 
AbstractObjectWriter::writeString(IN(RString) str)
{
  if (joinStrings() == false)
  {
    writeStringImpl(str);
    return;
  }
  acdk::lang::Object val = _stringCache->get(&str);
  if (val != Nil)
  {
    int lint = RInteger(val)->intValue();
    writeObjectLocalId(lint);
    DOUT("CS: " << str << " sref=" << lint);
    return;
  }
  writeObjectLocalId(_maxStringId);
  DOUT("NS: " << str << " sref=" << _maxStringId);
  _stringCache->put(&str, new (allocator()) Integer(_maxStringId));
  ++_maxStringId;
  writeStringImpl(str);
}

SerializedObjectDescriptor::SerializedObjectDescriptor(IN(RClass) cls)
: name(cls->getName())
, _class(cls)
, _serializedVersion(0)
{
  const acdk::lang::dmi::ClazzInfo* ci = cls->objectClazzInfo();
  ci->loadFullClazzInfo(false, false);

  int c = ci->getFieldsCount();
  fields = new SerializedFieldDescriptorArray(0);
  for (int i = 0; i < c; ++i)
  {
    const acdk::lang::dmi::ClazzFieldInfo* fi = ci->fields[i];
    fields[i] = new SerializedFieldDescriptor(fi->name, Class::getSingeltonClass(fi->type));
  }
}

//static 
RSerializedObjectDescriptor 
SerializedObjectDescriptor::lookup(IN(RClass) cl)
{
  return new SerializedObjectDescriptor(cl);
}

RString 
SerializedObjectDescriptor::toString()
{
  return getName();
}

} // io
} // acdk