MCommand.cpp
/*
 *
 * Copyright (c) 1996
 * Knowledge Science Institute, University of Calgary
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The Knowledge Science Institute makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 */

#define DONTDECLARECOMMANDLIBRARY 1
#include 
#undef DONTDECLARECOMMANDLIBRARY

#include 
#include 

#if defined(__BORLANDC__) && defined(__MT__)
#include  //needed for __threadid
#endif

ObjectLibrary < Command, 0 > *CommandLibrary = new ObjectLibrary < Command, 0 > (  );
ObjectLibrary < ObjectLocator, 0 > *ObjectLocatorLibrary = new ObjectLibrary < ObjectLocator, 0 > (  );

/*******************************************************************************
******************************** Command ***************************************
*******************************************************************************/
//
Command::Command( const ObjectLocator & obj, unsigned int id )
    {
    Locator = obj.clone(  );
    ID = id;
    User = ::getUser(  );
    ::time( &TimeStamp );
    PID = getpid(  );
#if defined(__BORLANDC__) && defined(__MT__)
    Thread = _threadid;
#else //not sure how to get the thread on other platforms
  Thread = 0;
#endif
    }

Command::Command(  )                    // use only as an intermediate step!!
    {
    ID = 0;
    Locator = NULL;
    User = ::getUser(  );
    ::time( &TimeStamp );
    PID = getpid(  );
#if defined(__BORLANDC__) && defined(__MT__)
    Thread = _threadid;
#else //not sure how to get the thread on other platforms
  Thread = 0;
#endif
    }

Command::Command( const Command & c )
    {
    ID = c.ID;
    Locator = c.Locator ? c.Locator->clone(  ) : NULL;
    User = c.User;
    TimeStamp = c.TimeStamp;
    PID = c.PID;
    Thread = c.Thread;
    }

Command & Command::operator = ( const Command & c )
    {
    ID = c.ID;
    if ( Locator )
        delete  Locator;
    Locator = c.Locator ? c.Locator->clone(  ) : NULL;
    User = c.User;
    TimeStamp = c.TimeStamp;
    PID = c.PID;
    Thread = c.Thread;
    return *this;
    }

int     Command::operator < ( Command & c )
    {
    int     ret = ( TimeStamp < c.TimeStamp );
    if ( ( !ret ) && !( c.TimeStamp < TimeStamp ) )     // tie-break equal times
        ret = ( User == c.User ) ? ( ( PID == c.PID ) ? ( Thread < c.Thread ) : ( PID < c.PID ) ) : ( User < c.User );
    return ret;
    }

int     Command::operator == ( Command & c )
    {
    return ( TimeStamp == c.TimeStamp ) && ( User == c.User ) && ( PID == c.PID ) && ( Thread == c.Thread );
    }

int     Command::isMine(  )
    {
    return User == ::getUser(  )
    && PID == getpid(  )
#if defined(__BORLANDC__) && defined(__MT__)
    && Thread == _threadid
#endif
    ;
    }

int     Command::execute(  )
    {
    CommandReceiver *r = Locator->getObject(  );
    if ( r )
        return r->execute( ( Command * ) this );
    CommandReceiverContainer *c = Locator->getSuper(  );
    if ( c )
        return c->executeOnBehalf( ( Command * ) this );
    return -300;
    }

int     Command::undo(  )
    {
    CommandReceiver *r = Locator->getObject(  );
    if ( r )
        return r->undo( ( Command * ) this );
    CommandReceiverContainer *c = Locator->getSuper(  );
    if ( c )
        return c->undoOnBehalf( ( Command * ) this );
    return -300;
    }

void    Command::printOn( ostream & o )
    {
    // this part can't have a counterpart in readFrom because we don't know all the types...
    Lexer::writeQuotedString( o, typeid( *this ).name(  ) );
    Lexer::writeDelim( o );

    Lexer::writeUnsigned( o, ID );
    Lexer::writeDelim( o );
    MASSERT( Locator );
    Lexer::writeQuotedString( o, typeid( *Locator ).name(  ) );
    Lexer::writeDelim( o );
    Locator->printOn( o );
    Lexer::writeDelim( o );
    Lexer::writeQuotedString( o, User );
    Lexer::writeDelim( o );
    Lexer::writeUnsigned( o, TimeStamp );
    Lexer::writeDelim( o );
    Lexer::writeUnsigned( o, PID );
    Lexer::writeDelim( o );
    Lexer::writeUnsigned( o, Thread );
    }

void    Command::readFrom( istream & i )
    {
    unsigned long val;
    Lexer::readUnsigned( i, val );
    ID = val;
    Lexer::readDelim( i );
    string  ltype;
    Lexer::readQuotedString( i, ltype );
    Lexer::readDelim( i );
    MASSERT( !Locator );
    Locator = ObjectLocatorLibrary->makeCopy( ltype );
    if ( !Locator )
        {
        error( 0, 'E', "Command::readFrom: Can't find %s in ObjectLocatorLibrary", ltype.c_str(  ) );
        }
    else
        {
        Locator->readFrom( i );
        Lexer::readDelim( i );
        Lexer::readQuotedString( i, User );
        Lexer::readDelim( i );
        unsigned long t;
        Lexer::readUnsigned( i, t );
        TimeStamp = t;
        Lexer::readDelim( i );
        Lexer::readUnsigned( i, t );
        PID = t;
        Lexer::readDelim( i );
        Lexer::readUnsigned( i, t );
        Thread = t;
        }
    }

Command *Command::readCommand( istream & i )
    {
    string  type;
    Lexer::readQuotedString( i, type );
    Lexer::readDelim( i );
    Command *ret = CommandLibrary->makeCopy( type );
    if ( !ret )
        {
        error( 0, 'E', "Command::readCommand: Can't find %s in CommandLibrary", type.c_str(  ) );
        }
    else
        {
        ret->readFrom( i );
        }
    return ret;
    }

/*******************************************************************************
**************************** CompositeCommand **********************************
*******************************************************************************/
//

CompositeCommand::CompositeCommand( const CompositeCommand & c ):Command( c )
    {
    for ( Commands_type::const_iterator i = c.Commands.begin(  ); i != c.Commands.end(  ); i++ )
        Commands.push_back( ( *i )->clone(  ) );
    }

CompositeCommand & CompositeCommand::operator = ( const CompositeCommand & c )
    {
    emptyTheTrash(  );
    Command::operator = ( c );
    for ( Commands_type::const_iterator i = c.Commands.begin(  ); i != c.Commands.end(  ); i++ )
        Commands.push_back( ( *i )->clone(  ) );
    return *this;
    }

int     CompositeCommand::execute(  )
    {
    for ( Commands_type::iterator i = Commands.begin(  ); i != Commands.end(  ); i++ )
        ( *i )->execute(  );
    return 0;
    }

int     CompositeCommand::undo(  )
    {
    for ( Commands_type::reverse_iterator i = Commands.rbegin(  ); i != Commands.rend(  ); i++ )
        ( *i )->undo(  );
    return 0;
    }

void    CompositeCommand::emptyTheTrash(  )
    {
    for ( Commands_type::iterator i = Commands.begin(  ); i != Commands.end(  ); i++ )
        {
        ( *i )->emptyTheTrash(  );
        delete( *i );
        }
    Commands.erase( Commands.begin(  ), Commands.end(  ) );
    }

void    CompositeCommand::printOn( ostream & o )
    {
    Command::printOn( o );
    char    delim = '(';
    for ( Commands_type::iterator i = Commands.begin(  ); i != Commands.end(  ); i++ )
        {
        Lexer::writeDelim( o, delim );
        delim = ',';
        ( *i )->printOn( o );
        }
    Lexer::writeDelim( o, ')' );
    }

void    CompositeCommand::readFrom( istream & i )
    {
    Command::readFrom( i );
    char    delim = '(';
    while ( !Lexer::readDelim( i, delim ) )
        {
        delim = ',';
        string  type;
        Lexer::readQuotedString( i, type );
        Lexer::readDelim( i );
        Command *com = CommandLibrary->makeCopy( type );
        if ( !com )
            {
            error( 0, 'E', "CompositeCommand::readFrom: Can't find %s in CommandLibrary", type.c_str(  ) );
            Lexer::scanPastDelim( i, ')' );
            }
        else
            {
            com->readFrom( i );
            Commands.push_back( com );
            Lexer::readDelim( i, ')' );
            }
        }
    }

void    CompositeCommand::setLocatorRoot( void *p )
    {
    Command::setLocatorRoot( p );
    for ( Commands_type::iterator i = Commands.begin(  ); i != Commands.end(  ); i++ )
        ( *i )->setLocatorRoot( p );
    }

/*******************************************************************************
**************************** CommandHandler ************************************
*******************************************************************************/
//

int CommandHandler::undo( void )
  {
  int     ret = -589;
  if ( HistoryPtr ) {
    ret = HistoryList[--HistoryPtr]->undo(  );
    }
  return ret;
  }

CommandHandler::~CommandHandler(  )
    {
    resetHistoryList(  );
    }

int     CommandHandler::resetHistoryList( void )
    {
    while ( HistoryList.size(  ) )
        {
        HistoryList.back(  )->emptyTheTrash(  );
        delete  HistoryList.back(  );
        HistoryList.pop_back(  );
        }
    HistoryPtr = 0;
    }

int CommandHandler::handleCommand( Command * c )
    {
    int ret = -787;
    MASSERT( c );
    ret = c->execute(  );
    if ( ret >= 0 ) { // only put the command on the history list or batchStack if the command succeeded.
      if (BatchStack.size()) {
        BatchStack.top()->append(c);
        }
      else {
        doPostCommandProcessing(c);
        }
      }
    return ret;
    }

int CommandHandler::doPostCommandProcessing(Command* c)
  {
  int ret = AppendToHistoryList(c);
  return ret;
  }

int CommandHandler::AppendToHistoryList(Command* c)
  {
  int ret = 0;
  while ( HistoryPtr < HistoryList.size(  ) ) {// HistoryPtr always points one PAST the 'current place' in HistoryList
    HistoryList.back(  )->emptyTheTrash(  );
    delete  HistoryList.back(  );
    HistoryList.pop_back(  );
    }
  HistoryList.push_back( c );
  HistoryPtr++;
  return ret;
  }

int CommandHandler::redo( void )
  {
  int     ret = -589;
  if (HistoryPtr < HistoryList.size()) {
    ret = HistoryList[HistoryPtr++]->execute(  );
    }
  return ret;
  }

int CommandHandler::beginBatch( unsigned int id )
  {
  int ret = -589;
  BatchStack.push(new CompositeCommand(id));
  return ret;
  }

int CommandHandler::endBatch( void )
  {
  int ret = -589;
  if (BatchStack.size()) {
    CompositeCommand* c = BatchStack.top();
    BatchStack.pop();
    doPostCommandProcessing(c);
    }
  return ret;
  }

int CommandHandler::AbortBatch(int undoIt)
  {
  int ret = -589;
  while (BatchStack.size()) {
    CompositeCommand* c = BatchStack.top();
    BatchStack.pop();
    if (undoIt) c->undo();
    delete c;
    }
  return ret;
  }