2005/5/9

     
 

XMLDelegate.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/acdkx_orb/src/acdkx/arb/XMLDelegate.cpp,v 1.22 2005/02/05 10:45:39 kommer Exp $

#include "XMLDelegate.h"
#include "AObjectImpl.h"
#include <acdk/io/CharArrayWriter.h>
#include <acdk/io/AbstractFilterReader.h>
#include <acdk/io/TeeWriter.h>

#include <acdk/xml/XMLTokenizer.h>
#include <acdk/xml/XMLObjectWriter.h>
#include <acdk/xml/XMLObjectReader.h>

namespace acdkx {
namespace arb {

using namespace acdk::lang::dmi;

USING_CLASS(acdk::io::, CharArrayWriter);
USING_CLASS(acdk::io::, AbstractFilterReader);
USING_CLASS(acdk::io::, FilterReader);
USING_CLASS(acdk::io::, TeeWriter);

USING_CLASS(acdk::xml::, XMLTokenizer);
USING_CLASS(acdk::xml::, XMLObjectWriter);
USING_CLASS(acdk::xml::, XMLObjectReader);

enum ETagOpen
{
  Open,
  Close, 
  Empty
};

void 
readTag(IN(RXMLTokenizer) tin, IN(RString) name, ETagOpen st)
{
  int tk = tin->nextToken();
  if (tk != XMLTokenizer::TOK_LT) 
    THROW1(Exception, "expect '<'");
  tk = tin->nextToken();
  if (st == Close) {
    if (tk != XMLTokenizer::TOK_SLASH) 
      THROW1(Exception, "expect '/'");
    tk = tin->nextToken();
  }
  if (tk != XMLTokenizer::TOK_SYMBOL) 
    THROW1(Exception, "expect Symbol");
  if (tin->element()->compareTo(name) != 0)
    THROW1(Exception, "expect tag " + name);
  tk = tin->nextToken();
  if (st == Empty) {
    if (tk != XMLTokenizer::TOK_SLASH) 
      THROW1(Exception, "expect '/'");
    tk = tin->nextToken();
  }
  if (tk != XMLTokenizer::TOK_GT) 
    THROW1(Exception, "expect '>'");
}


RString 
readTag(IN(RXMLTokenizer) tin, ETagOpen& st)
{
  st = Open;
  int tk = tin->nextToken();
  if (tk != XMLTokenizer::TOK_LT) 
    THROW1(Exception, "expect '<'");
  tk = tin->nextToken();
  if (tk == XMLTokenizer::TOK_SLASH) {
    
    st = Close;
    tk = tin->nextToken();
  }
  RString erg = tin->element();
  tk = tin->nextToken();
  if (tk == XMLTokenizer::TOK_SLASH) {
    st = Empty;
    tk = tin->nextToken();
  }
  if (tk != XMLTokenizer::TOK_GT) 
    THROW1(Exception, "expect '>'");
  return erg;
}


ScriptVar
readArg(IN(RARB) arb, IN(RXMLTokenizer) tin)
{
  ScriptVar sv = XMLObjectReader(tin).readScriptVar();
  if (sv.type == ScriptVar::ObjectType) {
    acdk::lang::Object obj = sv.getObjectVar();
    if (instanceof(obj, ::acdk::xml::RemoteInterface) == true) {
      sv = arb->string_to_object(::acdk::xml::RRemoteInterface(obj)->objectId());
    }
  }
  return sv;
}

void 
readArgs(IN(RARB) arb, IN(RXMLTokenizer) tin, ScriptVarArray& args, ScriptVar& ex)
{
  int tk = tin->nextToken();
  if (tk != XMLTokenizer::TOK_LT) 
    THROW1(Exception, "expect '<'");
  tk = tin->nextToken();
  if (tk != XMLTokenizer::TOK_SYMBOL) 
    THROW1(Exception, "expect Symbol");
  
  if (tin->element()->equals("exception") == true) {
    tk = tin->nextToken();
    if (tk != XMLTokenizer::TOK_GT) 
      THROW1(Exception, "expect '>'");
    ex = XMLObjectReader(tin).readScriptVar();
    readTag(tin, "exception", Close);
    return;
  } else if (tin->element()->equals("args") == false) 
    THROW1(Exception, "expect 'args' or 'exception as tag");
  tk = tin->nextToken();
  if (tk != XMLTokenizer::TOK_GT) 
    THROW1(Exception, "expect '>'");
  do {
    ETagOpen st;
    RString ntag = readTag(tin, st);
    if (st == Open && ntag->compareTo("arg") == 0) {
      args.push_back(readArg(arb, tin));
      readTag(tin, "arg", Close);
    } else if (st == Close && ntag->compareTo("args") == 0) {
      break;
    }
  } while (true);
}

RString 
readText(IN(RXMLTokenizer) tin)
{
  int tk = tin->nextToken();
  if (tk != XMLTokenizer::TOK_TEXT)
    THROW1(Exception, "expect Text");
  return tin->element();
}


void 
XMLDelegate::readInvoke(IN(RARB) arb, IN(RXMLTokenizer) tin, IN(RString) name, OUT(RString) fromObject, OUT(RString) toObject, 
                        OUT(RString) funcname, ScriptVarArray& args, ::acdk::lang::dmi::ScriptVar& _theEx)
{
  readTag(tin, name, Open);
  readTag(tin, "objectid", Open);
  
  toObject = readText(tin);
  readTag(tin, "objectid", Close);
  readTag(tin, "replyto", Open);
  fromObject = readText(tin);
  readTag(tin, "replyto", Close);
  readTag(tin, "function", Open);
  funcname = readText(tin);
  readTag(tin, "function", Close);
  readArgs(arb, tin, args, _theEx);
  readTag(tin, name, Close);
}


ACDK_DECL_CLASS(TeeReader);
class TeeReader //#### in acdk_core
: public AbstractFilterReader,
  implements FilterReader
{
  RWriter _out;
public:
  TeeReader(IN(RReader) from, IN(RWriter) to)
  : AbstractFilterReader(from),
    _out(to)
  {
  }
  virtual int read()
  {
    int i = _in->read();
    if (i == -1)
      return i;
    _out->write((unsigned char)i);
    return i;
  }
  virtual int read(IN(RbyteArray) buffer, int offset = 0, int len = -1)
  {
    int erg = _in->read(buffer, offset, len);
    if (erg != 0)
      _out->write(buffer, offset, erg);
    return erg;
  }
  virtual int read(byte* buffer, int offset, int len)
  {
    int erg = _in->read(buffer, offset, len);
    if (erg != 0)
      _out->write(buffer, offset, erg);
    return erg;
  }
};


enum CallDirection
{
  InCall,
  OutCall
};

#define write_string(chars) write((const byte*)chars, 0, strlen(chars)) // ### @todo FIXME

void 
writeArg(IN(RARB) arb, IN(RXMLObjectWriter) xmlout, ScriptVar& arg, int flags)
{
  RWriter out = xmlout->getOut();
  out->write_string("<arg>");
  if (arg.type == ScriptVar::ObjectType) {
    acdk::lang::Object o = arg.getObjectVar();
    if (flags & MiAiByval || o->getClass() == String::GetClass()) {
      xmlout->writeObject( o);
    } else if (instanceof(o, AObjectImpl) == true) {
      out->write_string("<interface>");
      out->write_string(arb->object_to_string(o)->c_str());
      out->write_string("</interface>");
    } else {
      THROW1(Exception, "Unsupported class (not AObjectImpl):" + o->getClass()->getName());
      
    }
    
  } else {
    xmlout->writeScriptVar(arg);
  }
  out->write_string("</arg>");
}

void 
writeArgs(IN(RARB) arb, IN(RXMLObjectWriter) xmlout, ::acdk::lang::dmi::ScriptVarArray& args, 
          const dmi::ClazzMethodInfo* cmi, CallDirection cd)
{
  RWriter out = xmlout->getOut();
  out->write_string("<args>");
  int i = 0;
  int argidx = 0;
  if (cd == InCall && argidx == 0 && cmi->returnType != ::acdk::lang::dmi::ClazzInfo::getVoidClazz()) {
    writeArg(arb, xmlout, args[i], cmi->flags);
  }
  while (cmi->methodArgs[i] != 0) 
  {
    if ((cd == OutCall && cmi->methodArgs[i]->flags & MiAiIn) ||
        (cd == InCall && cmi->methodArgs[i]->flags & MiAiOut)) 
    { 
      writeArg(arb, xmlout, args[i], cmi->methodArgs[i]->flags);
      ++argidx;
    }
    i++;
  }
  out->write_string("</args>");

}

//virtual 
void 
XMLDelegate::invoke(IN(RARB) arb, IN(RObjectID) objid, 
                    const ::acdk::lang::dmi::ClazzMethodInfo* cmi, 
                    ::acdk::lang::dmi::ScriptVarArray& args, 
                    ::acdk::lang::dmi::ScriptVarArray& ergs,
                    ::acdk::lang::dmi::ScriptVar& _theEx)
{
  RConnection con = arb->connect(objid);
  RWriter wout = con->out();
  RReader win = con->in();

  RTeeReader teein = new TeeReader(&con->in(), &System::out->getWriter());
  RTeeWriter teeout = new TeeWriter(&con->out(), &System::out->getWriter());
  wout = (RWriter)teeout;
  win = (RReader)teein;
  /*RWriter wout = System::out;
  RReader win = System::in;
  */
  //RCharArrayWriter rout = new CharArrayWriter(2048);
  Writer& out = *wout.iptr();
  /*
    <invoke>
      <objectid>objid</objectid>
      <replyto>objid</replyto>
      <function>name</function>
      optional <exception>name</exception>
      <args>
        <arg><int>123</int><arg>
      </args>
    </invoke>
  */
  out.write_string("<invoke><objectid>");
  out.write_string(objid->toString()->c_str());
  out.write_string("</objectid><replyto>");
  out.write_string(arb->object_to_string((acdk::lang::Object)arb)->c_str());
  out.write_string("</replyto><function>");
  out.write_string(cmi->name);
  out.write_string("</function>");
    
  XMLObjectWriter xmlout(wout);
  writeArgs(arb, SR(XMLObjectWriter, xmlout), args, cmi, OutCall);

  
  out.write_string("</invoke>");
  out.flush();
  if (cmi->flags & MiMiOneway)
    return;
  /*
    <invoke-reply>
      <objectid>objid</objectid>
      <replyto>objid</replyto>
      <function>name</function>
      <args>
        <arg><int>123</int><arg>
      </args>
    </invoke-reply>
  */
  RXMLTokenizer tin = new XMLTokenizer(win);
  RString fromObject;
  RString toObject;
  RString function;
  readInvoke(arb, tin, "invoke-reply", fromObject, toObject, function, ergs, _theEx);
}




//virtual 
void 
XMLDelegate::dispatch(IN(RARB) arb, IN(::acdk::io::RReader) in, IN(::acdk::io::RWriter) outw)
{
  RWriter out = outw;
  RTeeReader teein = new TeeReader(&in, &System::out->getWriter());
  RTeeWriter teeout = new TeeWriter(&out, &System::out->getWriter());
  RXMLTokenizer tin = new XMLTokenizer(&teein);
  out = (RWriter)teeout;
  ScriptVarArray args(0);
  RString fromObject;
  RString toObject;
  RString funcname;
  ScriptVar dummyEx;
  readInvoke(arb, tin, "invoke", fromObject, toObject, funcname, args, dummyEx); // dummyEx should never readed
  acdk::lang::Object lobj = arb->string_to_object(toObject);
  ScriptVarArray ergs(0);
  ScriptVar ex;
  const ::acdk::lang::dmi::ClazzMethodInfo* cmi = RArbInterface(lobj)->orbDispatch(funcname->c_str(), args, ergs, ex);
  if (cmi->flags & MiMiOneway) {
    return;
  }
  out->write_string("<invoke-reply><objectid>");
  out->write_string(fromObject->c_str());
  out->write_string("</objectid><replyto>");
  out->write_string(toObject->c_str());
  out->write_string("</replyto><function>");
  out->write_string(funcname->c_str());
  out->write_string("</function>");
   
  XMLObjectWriter xmlout(out);
  if (ex.type == ScriptVar::ObjectType) {
    out->write_string("<exception>");
    xmlout.writeObject(ex.getObjectVar());
    out->write_string("</exception>");
  } else 
    writeArgs(arb, &xmlout, ergs, cmi, InCall);

  
  out->write_string("</invoke-reply>");
}


} // namespace arb 
} // namespace acdkx