mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1076 lines
32 KiB
C++
1076 lines
32 KiB
C++
/*=========================================================================
|
|
|
|
Program: ParaView
|
|
Module: vtkClientServerInterpreter.cxx
|
|
|
|
Copyright (c) Kitware, Inc.
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkClientServerInterpreter.h"
|
|
|
|
#include "vtkClientServerStream.h"
|
|
#include "vtkCommand.h"
|
|
#include "vtkDynamicLoader.h"
|
|
#include "vtkObjectFactory.h"
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <sys/stat.h>
|
|
|
|
vtkStandardNewMacro(vtkClientServerInterpreter);
|
|
|
|
//----------------------------------------------------------------------------
|
|
class vtkClientServerInterpreterInternals
|
|
{
|
|
public:
|
|
struct ContextInformation
|
|
{
|
|
ContextInformation(void* ctx, vtkContextFreeFunction freeFunction)
|
|
{
|
|
this->Context = ctx;
|
|
this->FreeFunction = freeFunction;
|
|
}
|
|
~ContextInformation()
|
|
{
|
|
if(this->FreeFunction)
|
|
{
|
|
this->FreeFunction(this->Context);
|
|
}
|
|
}
|
|
|
|
void* Context;
|
|
vtkContextFreeFunction FreeFunction;
|
|
};
|
|
template <typename FunctionType>
|
|
struct FunctionWithContext
|
|
{
|
|
FunctionWithContext(FunctionType function, ContextInformation* ctx)
|
|
{
|
|
this->Function = function;
|
|
this->Context = ctx;
|
|
}
|
|
~FunctionWithContext()
|
|
{
|
|
delete this->Context;
|
|
}
|
|
|
|
FunctionType Function;
|
|
ContextInformation* Context;
|
|
private:
|
|
FunctionWithContext(const FunctionWithContext&); // Not implemented.
|
|
FunctionWithContext& operator=(const FunctionWithContext&); // Not implemented.
|
|
};
|
|
typedef FunctionWithContext<vtkClientServerNewInstanceFunction> NewInstanceFunction;
|
|
typedef FunctionWithContext<vtkClientServerCommandFunction> CommandFunction;
|
|
typedef std::map<std::string, const NewInstanceFunction*> NewInstanceFunctionsType;
|
|
typedef std::map<std::string, const CommandFunction*> ClassToFunctionMapType;
|
|
typedef std::map<vtkTypeUInt32, vtkClientServerStream*> IDToMessageMapType;
|
|
NewInstanceFunctionsType NewInstanceFunctions;
|
|
ClassToFunctionMapType ClassToFunctionMap;
|
|
IDToMessageMapType IDToMessageMap;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkClientServerInterpreter::vtkClientServerInterpreter()
|
|
{
|
|
this->NextAvailableId = 0;
|
|
this->Internal = new vtkClientServerInterpreterInternals;
|
|
this->LastResultMessage = new vtkClientServerStream(this);
|
|
this->LogStream = 0;
|
|
this->LogFileStream = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkClientServerInterpreter::~vtkClientServerInterpreter()
|
|
{
|
|
// Delete any remaining instance functions.
|
|
vtkClientServerInterpreterInternals::NewInstanceFunctionsType::iterator ni;
|
|
for(ni = this->Internal->NewInstanceFunctions.begin();
|
|
ni != this->Internal->NewInstanceFunctions.end(); ++ni)
|
|
{
|
|
delete ni->second;
|
|
}
|
|
|
|
// Delete any remaining command functions.
|
|
vtkClientServerInterpreterInternals::ClassToFunctionMapType::iterator ci;
|
|
for(ci = this->Internal->ClassToFunctionMap.begin();
|
|
ci != this->Internal->ClassToFunctionMap.end(); ++ci)
|
|
{
|
|
delete ci->second;
|
|
}
|
|
|
|
// Delete any remaining messages.
|
|
vtkClientServerInterpreterInternals::IDToMessageMapType::iterator hi;
|
|
for(hi = this->Internal->IDToMessageMap.begin();
|
|
hi != this->Internal->IDToMessageMap.end(); ++hi)
|
|
{
|
|
delete hi->second;
|
|
}
|
|
|
|
// End logging.
|
|
this->SetLogStream(0);
|
|
|
|
delete this->LastResultMessage;
|
|
this->LastResultMessage = 0;
|
|
delete this->Internal;
|
|
this->Internal = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkObjectBase*
|
|
vtkClientServerInterpreter::GetObjectFromID(vtkClientServerID id, int noerror)
|
|
{
|
|
// Get the message corresponding to this ID.
|
|
if(const vtkClientServerStream* tmp = this->GetMessageFromID(id))
|
|
{
|
|
// Retrieve the object from the message.
|
|
vtkObjectBase* obj = 0;
|
|
if(tmp->GetNumberOfArguments(0) == 1 && tmp->GetArgument(0, 0, &obj))
|
|
{
|
|
return obj;
|
|
}
|
|
else
|
|
{
|
|
if ( !noerror )
|
|
{
|
|
vtkErrorMacro("Attempt to get an object for ID " << id.ID
|
|
<< " whose message does not contain exactly one object.");
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !noerror )
|
|
{
|
|
vtkErrorMacro("Attempt to get object for ID " << id.ID
|
|
<< " that is not present in the hash table.");
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkClientServerID
|
|
vtkClientServerInterpreter::GetIDFromObject(vtkObjectBase* key)
|
|
{
|
|
// Search the hash table for the given object.
|
|
vtkClientServerID result;
|
|
vtkClientServerInterpreterInternals::IDToMessageMapType::iterator hi;
|
|
for(hi = this->Internal->IDToMessageMap.begin();
|
|
hi != this->Internal->IDToMessageMap.end(); ++hi)
|
|
{
|
|
vtkObjectBase* obj;
|
|
if(hi->second->GetArgument(0, 0, &obj) && obj == key)
|
|
{
|
|
result.ID = hi->first;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkClientServerInterpreter::SetLogFile(const char* name)
|
|
{
|
|
// Close any existing log.
|
|
this->SetLogStream(0);
|
|
|
|
// If a non-empty name was given, open a new log file.
|
|
if(name && name[0])
|
|
{
|
|
this->LogFileStream = new ofstream(name);
|
|
if(this->LogFileStream && *this->LogFileStream)
|
|
{
|
|
this->LogStream = this->LogFileStream;
|
|
}
|
|
else
|
|
{
|
|
vtkErrorMacro("Error opening log file \"" << name << "\" for writing.");
|
|
if(this->LogFileStream)
|
|
{
|
|
delete this->LogFileStream;
|
|
this->LogFileStream = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkClientServerInterpreter::SetLogStream(ostream* ostr)
|
|
{
|
|
if(ostr != this->LogStream)
|
|
{
|
|
// Close the current log file, if any.
|
|
if(this->LogStream && this->LogStream == this->LogFileStream)
|
|
{
|
|
delete this->LogFileStream;
|
|
this->LogFileStream = 0;
|
|
}
|
|
|
|
// Set the log to use the given stream.
|
|
this->LogStream = ostr;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::ProcessStream(const unsigned char* msg,
|
|
size_t msgLength)
|
|
{
|
|
vtkClientServerStream css;
|
|
css.SetData(msg, msgLength);
|
|
return this->ProcessStream(css);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::ProcessStream(const vtkClientServerStream& css)
|
|
{
|
|
for(int i=0; i < css.GetNumberOfMessages(); ++i)
|
|
{
|
|
if(!this->ProcessOneMessage(css, i))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
vtkClientServerInterpreter::ProcessOneMessage(const vtkClientServerStream& css,
|
|
int message)
|
|
{
|
|
// Log the message.
|
|
if(this->LogStream)
|
|
{
|
|
*this->LogStream << "---------------------------------------"
|
|
<< "---------------------------------------\n";
|
|
*this->LogStream << "Processing ";
|
|
css.PrintMessage(*this->LogStream, message);
|
|
this->LogStream->flush();
|
|
}
|
|
|
|
// Look for known commands in the message.
|
|
int result = 0;
|
|
vtkClientServerStream::Commands cmd = css.GetCommand(message);
|
|
switch(cmd)
|
|
{
|
|
case vtkClientServerStream::New:
|
|
result = this->ProcessCommandNew(css, message); break;
|
|
case vtkClientServerStream::Invoke:
|
|
result = this->ProcessCommandInvoke(css, message); break;
|
|
case vtkClientServerStream::Delete:
|
|
result = this->ProcessCommandDelete(css, message); break;
|
|
case vtkClientServerStream::Assign:
|
|
result = this->ProcessCommandAssign(css, message); break;
|
|
default:
|
|
{
|
|
// Command is not known.
|
|
std::ostringstream error;
|
|
error << "Message with type "
|
|
<< vtkClientServerStream::GetStringFromCommand(cmd)
|
|
<< " cannot be executed." << ends;
|
|
this->LastResultMessage->Reset();
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << error.str().c_str()
|
|
<< vtkClientServerStream::End;
|
|
} break;
|
|
}
|
|
|
|
// Log the result of the message.
|
|
if(this->LogStream)
|
|
{
|
|
if(this->LastResultMessage->GetNumberOfMessages() > 0)
|
|
{
|
|
*this->LogStream << "Result ";
|
|
this->LastResultMessage->Print(*this->LogStream);
|
|
}
|
|
else
|
|
{
|
|
*this->LogStream << "Empty Result\n";
|
|
}
|
|
this->LogStream->flush();
|
|
}
|
|
|
|
// If the command failed with an error message, invoke the error
|
|
// event so observers can handle the error.
|
|
if(!result)
|
|
{
|
|
vtkClientServerInterpreterErrorCallbackInfo info = {&css, message};
|
|
this->InvokeEvent(vtkCommand::UserEvent, &info);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
vtkClientServerInterpreter
|
|
::ProcessCommandNew(const vtkClientServerStream& css, int midx)
|
|
{
|
|
// This command ignores any previous result.
|
|
this->LastResultMessage->Reset();
|
|
|
|
// Make sure we have some instance creation functions registered.
|
|
if(this->Internal->NewInstanceFunctions.empty())
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error
|
|
<< "Attempt to create object with no registered class wrappers."
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Get the class name and desired ID for the instance.
|
|
const char* cname = 0;
|
|
vtkClientServerID id;
|
|
if(css.GetNumberOfArguments(midx) == 2 &&
|
|
css.GetArgument(midx, 0, &cname) && css.GetArgument(midx, 1, &id))
|
|
{
|
|
// Make sure the given ID is valid.
|
|
if(id.ID == 0)
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << "Cannot create object with ID 0."
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Make sure the ID doesn't exist.
|
|
if(this->Internal->IDToMessageMap.find(id.ID) !=
|
|
this->Internal->IDToMessageMap.end())
|
|
{
|
|
std::ostringstream error;
|
|
error << "Attempt to create object with existing ID " << id.ID << "."
|
|
<< ends;
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << error.str().c_str()
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Find a NewInstance function that knows about the class.
|
|
int created = 0;
|
|
if(this->Internal->NewInstanceFunctions.count(cname))
|
|
{
|
|
const vtkClientServerInterpreterInternals::NewInstanceFunction* n =
|
|
this->Internal->NewInstanceFunctions[cname];
|
|
vtkClientServerNewInstanceFunction function = n->Function;
|
|
void* ctx = n->Context ? n->Context->Context : 0;
|
|
this->NewInstance(function(ctx),id);
|
|
created =1;
|
|
}
|
|
if(created)
|
|
{
|
|
// Object was created. Notify observers.
|
|
vtkClientServerInterpreter::NewCallbackInfo info;
|
|
info.Type = cname;
|
|
info.ID = id.ID;
|
|
this->InvokeEvent(vtkCommand::UserEvent+1, &info);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// Object was not created.
|
|
std::ostringstream error;
|
|
error << "Cannot create object of type \"" << cname << "\"." << ends;
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << error.str().c_str()
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error <<
|
|
"Invalid arguments to vtkClientServerStream::New. "
|
|
"There must be exactly two arguments. The first must be a string and "
|
|
"the second an id."
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
vtkClientServerInterpreter
|
|
::ProcessCommandInvoke(const vtkClientServerStream& css, int midx)
|
|
{
|
|
// Create a message with all known id_value arguments expanded.
|
|
vtkClientServerStream msg;
|
|
if(!this->ExpandMessage(css, midx, 0, msg))
|
|
{
|
|
// ExpandMessage left an error in the LastResultMessage for us.
|
|
return 0;
|
|
}
|
|
|
|
// Now that id_values have been expanded, we do not need the last
|
|
// result. Reset the result to empty before processing the message.
|
|
this->LastResultMessage->Reset();
|
|
|
|
// Get the object and method to be invoked.
|
|
vtkObjectBase* obj;
|
|
const char* method;
|
|
if(msg.GetNumberOfArguments(0) >= 2 &&
|
|
msg.GetArgument(0, 0, &obj) && msg.GetArgument(0, 1, &method))
|
|
{
|
|
// Log the expanded form of the message.
|
|
if(this->LogStream)
|
|
{
|
|
*this->LogStream << "Invoking ";
|
|
msg.Print(*this->LogStream);
|
|
this->LogStream->flush();
|
|
}
|
|
|
|
// Find the command function for this object's type.
|
|
if(obj && this->HasCommandFunction(obj->GetClassName()))
|
|
{
|
|
if (this->CallCommandFunction(obj->GetClassName(), obj, method, msg,
|
|
*this->LastResultMessage))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Command function was not found for the class.
|
|
std::ostringstream error;
|
|
const char* cname = obj? obj->GetClassName():"(vtk object is NULL)";
|
|
error << "Wrapper function not found for class \"" << cname << "\"."
|
|
<< ends;
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << error.str().c_str()
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error <<
|
|
"Invalid arguments to vtkClientServerStream::Invoke. "
|
|
"There must be at least two arguments. The first must be an object "
|
|
"and the second a string."
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
vtkClientServerInterpreter
|
|
::ProcessCommandDelete(const vtkClientServerStream& msg, int midx)
|
|
{
|
|
// This command ignores any previous result.
|
|
if(this->LastResultMessage == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
this->LastResultMessage->Reset();
|
|
|
|
// Get the ID to delete.
|
|
vtkClientServerID id;
|
|
if(msg.GetNumberOfArguments(midx) == 1 && msg.GetArgument(midx, 0, &id))
|
|
{
|
|
// Make sure the given ID is valid.
|
|
if(id.ID == 0)
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << "Cannot delete object with ID 0."
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Find the ID in the map.
|
|
vtkClientServerStream* item = 0;
|
|
vtkClientServerInterpreterInternals::IDToMessageMapType::iterator itemi;
|
|
itemi = this->Internal->IDToMessageMap.find(id.ID);
|
|
if(itemi != this->Internal->IDToMessageMap.end())
|
|
{
|
|
item = itemi->second;
|
|
}
|
|
else
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error
|
|
<< "Attempt to delete ID that does not exist."
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// If the value is an object, notify observers of deletion.
|
|
vtkObjectBase* obj;
|
|
if(item->GetArgument(0, 0, &obj) && obj)
|
|
{
|
|
vtkClientServerInterpreter::NewCallbackInfo info;
|
|
info.Type = obj->GetClassName();
|
|
info.ID = id.ID;
|
|
this->InvokeEvent(vtkCommand::UserEvent+2, &info);
|
|
}
|
|
|
|
// Remove the ID from the map.
|
|
this->Internal->IDToMessageMap.erase(id.ID);
|
|
|
|
// Delete the entry's value.
|
|
delete item;
|
|
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error <<
|
|
"Invalid arguments to vtkClientServerStream::Delete. "
|
|
"There must be exactly one argument and it must be an id."
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
vtkClientServerInterpreter
|
|
::ProcessCommandAssign(const vtkClientServerStream& css, int midx)
|
|
{
|
|
// Create a message with all known id_value arguments expanded
|
|
// except for the first argument.
|
|
vtkClientServerStream msg;
|
|
if(!this->ExpandMessage(css, midx, 1, msg))
|
|
{
|
|
// ExpandMessage left an error in the LastResultMessage for us.
|
|
return 0;
|
|
}
|
|
|
|
// Now that id_values have been expanded, we do not need the last
|
|
// result. Reset the result to empty before processing the message.
|
|
this->LastResultMessage->Reset();
|
|
|
|
// Make sure the first argument is an id.
|
|
vtkClientServerID id;
|
|
if(msg.GetNumberOfArguments(0) >= 1 && msg.GetArgument(0, 0, &id))
|
|
{
|
|
// Make sure the given ID is valid.
|
|
if(id.ID == 0)
|
|
{
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << "Cannot assign to ID 0."
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Make sure the ID doesn't exist.
|
|
if(this->Internal->IDToMessageMap.find(id.ID) !=
|
|
this->Internal->IDToMessageMap.end())
|
|
{
|
|
std::ostringstream error;
|
|
error << "Attempt to assign existing ID " << id.ID << "." << ends;
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << error.str().c_str()
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Copy the expanded message to the result message except for the
|
|
// first argument.
|
|
*this->LastResultMessage << vtkClientServerStream::Reply;
|
|
for(int a=1; a < msg.GetNumberOfArguments(0); ++a)
|
|
{
|
|
*this->LastResultMessage << msg.GetArgument(0, a);
|
|
}
|
|
*this->LastResultMessage << vtkClientServerStream::End;
|
|
|
|
// Copy the result to store it in the map. The result itself
|
|
// remains unchanged.
|
|
vtkClientServerStream* tmp;
|
|
tmp = new vtkClientServerStream(*this->LastResultMessage, this);
|
|
this->Internal->IDToMessageMap[id.ID] = tmp;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
this->LastResultMessage->Reset();
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error <<
|
|
"Invalid arguments to vtkClientServerStream::Assign. "
|
|
"There must be at least one argument and it must be an id."
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::ExpandMessage(const vtkClientServerStream& in,
|
|
int inIndex, int startArgument,
|
|
vtkClientServerStream& out)
|
|
{
|
|
// Reset the output and make sure we have input.
|
|
out.Reset();
|
|
if(inIndex < 0 || inIndex >= in.GetNumberOfMessages())
|
|
{
|
|
std::ostringstream error;
|
|
error << "ExpandMessage called to expand message index " << inIndex
|
|
<< " in a stream with " << in.GetNumberOfMessages()
|
|
<< " messages." << ends;
|
|
this->LastResultMessage->Reset();
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Error << error.str().c_str()
|
|
<< vtkClientServerStream::End;
|
|
return 0;
|
|
}
|
|
|
|
// Copy the command.
|
|
out << in.GetCommand(inIndex);
|
|
|
|
// Just copy the first arguments.
|
|
int a;
|
|
for(a=0; a < startArgument && a < in.GetNumberOfArguments(inIndex); ++a)
|
|
{
|
|
out << in.GetArgument(inIndex, a);
|
|
}
|
|
|
|
// Expand id_value for remaining arguments.
|
|
for(a=startArgument; a < in.GetNumberOfArguments(inIndex); ++a)
|
|
{
|
|
if(in.GetArgumentType(inIndex, a) == vtkClientServerStream::id_value)
|
|
{
|
|
vtkClientServerID id;
|
|
in.GetArgument(inIndex, a, &id);
|
|
|
|
// If the ID is in the map, expand it. Otherwise, leave it.
|
|
if(const vtkClientServerStream* tmp = this->GetMessageFromID(id))
|
|
{
|
|
for(int b=0; b < tmp->GetNumberOfArguments(0); ++b)
|
|
{
|
|
out << tmp->GetArgument(0, b);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out << in.GetArgument(inIndex, a);
|
|
}
|
|
}
|
|
else if(in.GetArgumentType(inIndex, a) ==
|
|
vtkClientServerStream::LastResult)
|
|
{
|
|
// Insert the last result value.
|
|
for(int b=0; b < this->LastResultMessage->GetNumberOfArguments(0); ++b)
|
|
{
|
|
out << this->LastResultMessage->GetArgument(0, b);
|
|
}
|
|
}
|
|
else if (in.GetArgumentType(inIndex, a) ==
|
|
vtkClientServerStream::stream_value)
|
|
{
|
|
// Evaluate the expression and insert the result.
|
|
vtkClientServerStream *lastResult = this->LastResultMessage;
|
|
this->LastResultMessage = new vtkClientServerStream();
|
|
vtkClientServerStream substream;
|
|
in.GetArgument(inIndex, a, &substream);
|
|
if (this->ProcessStream(substream))
|
|
{
|
|
// Insert the last result value.
|
|
for(int b=0; b < this->LastResultMessage->GetNumberOfArguments(0); ++b)
|
|
{
|
|
out << this->LastResultMessage->GetArgument(0, b);
|
|
}
|
|
}
|
|
// restore last-result
|
|
delete this->LastResultMessage;
|
|
this->LastResultMessage = lastResult;
|
|
}
|
|
else
|
|
{
|
|
// Just copy the argument.
|
|
out << in.GetArgument(inIndex, a);
|
|
}
|
|
}
|
|
|
|
// End the message.
|
|
out << vtkClientServerStream::End;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const vtkClientServerStream*
|
|
vtkClientServerInterpreter::GetMessageFromID(vtkClientServerID id)
|
|
{
|
|
// Find the message in the map.
|
|
vtkClientServerInterpreterInternals::IDToMessageMapType::iterator tmp;
|
|
tmp = this->Internal->IDToMessageMap.find(id.ID);
|
|
if(id.ID > 0 && tmp != this->Internal->IDToMessageMap.end())
|
|
{
|
|
return tmp->second;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const vtkClientServerStream& vtkClientServerInterpreter::GetLastResult() const
|
|
{
|
|
return *this->LastResultMessage;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::NewInstance(vtkObjectBase* obj,
|
|
vtkClientServerID id)
|
|
{
|
|
// Store the object in the last result.
|
|
this->LastResultMessage->Reset();
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Reply << obj << vtkClientServerStream::End;
|
|
|
|
// Last result holds a reference. Remove reference from ::New()
|
|
// call in generated code.
|
|
obj->Delete();
|
|
|
|
// Copy the result to store it in the map. The result itself
|
|
// remains unchanged. The ProcessCommandNew method already checked
|
|
// that the id does not exist in the map, so the insertion does not
|
|
// have to be checked.
|
|
vtkClientServerStream* entry =
|
|
new vtkClientServerStream(*this->LastResultMessage, this);
|
|
this->Internal->IDToMessageMap[id.ID] = entry;
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
class vtkClientServerInterpreterCommand: public vtkCommand
|
|
{
|
|
public:
|
|
static vtkClientServerInterpreterCommand* New()
|
|
{ return new vtkClientServerInterpreterCommand; }
|
|
void Execute(vtkObject*, unsigned long, void *)
|
|
{ this->Interpreter->ProcessStream(this->Stream); }
|
|
|
|
vtkClientServerStream Stream;
|
|
vtkClientServerInterpreter* Interpreter;
|
|
protected:
|
|
vtkClientServerInterpreterCommand() {}
|
|
~vtkClientServerInterpreterCommand() {}
|
|
private:
|
|
vtkClientServerInterpreterCommand(const vtkClientServerInterpreterCommand&);
|
|
void operator=(const vtkClientServerInterpreterCommand&);
|
|
};
|
|
|
|
int vtkClientServerInterpreter::NewObserver(vtkObject* obj, const char* event,
|
|
const vtkClientServerStream& css)
|
|
{
|
|
// Create the command object and add the observer.
|
|
vtkClientServerInterpreterCommand* cmd =
|
|
vtkClientServerInterpreterCommand::New();
|
|
cmd->Stream = css;
|
|
cmd->Interpreter = this;
|
|
unsigned long id = obj->AddObserver(event, cmd);
|
|
cmd->Delete();
|
|
|
|
// Store the observer id as the last result.
|
|
this->LastResultMessage->Reset();
|
|
*this->LastResultMessage
|
|
<< vtkClientServerStream::Reply << id << vtkClientServerStream::End;
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
vtkClientServerInterpreter
|
|
::AddCommandFunction(const char* cname, vtkClientServerCommandFunction func,
|
|
void* ctx, vtkContextFreeFunction freeFunction)
|
|
{
|
|
vtkClientServerInterpreterInternals::ClassToFunctionMapType::const_iterator it =
|
|
this->Internal->ClassToFunctionMap.find(cname);
|
|
|
|
if(it != this->Internal->ClassToFunctionMap.end())
|
|
{
|
|
if(it->second->Context == ctx && it->second->Function == func)
|
|
{ // we already have this EXACT definition available so don't add
|
|
return;
|
|
}
|
|
vtkErrorMacro("Ignoring duplicate command function for " << cname);
|
|
return;
|
|
}
|
|
|
|
vtkClientServerInterpreterInternals::ContextInformation* context = NULL;
|
|
if(ctx || freeFunction)
|
|
{
|
|
context = new vtkClientServerInterpreterInternals::ContextInformation(ctx, freeFunction);
|
|
}
|
|
|
|
this->Internal->ClassToFunctionMap[cname] =
|
|
new vtkClientServerInterpreterInternals::CommandFunction(func, context);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
vtkClientServerInterpreter::HasCommandFunction(const char* cname)
|
|
{
|
|
if (!cname)
|
|
{
|
|
return false;
|
|
}
|
|
return (this->Internal->ClassToFunctionMap.count(cname) > 0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
vtkClientServerInterpreter::CallCommandFunction(const char* cname,
|
|
vtkObjectBase* ptr,
|
|
const char* method,
|
|
const vtkClientServerStream& msg,
|
|
vtkClientServerStream& result)
|
|
{
|
|
vtkClientServerInterpreterInternals::ClassToFunctionMapType::const_iterator f =
|
|
this->Internal->ClassToFunctionMap.find(cname);
|
|
|
|
if (f == this->Internal->ClassToFunctionMap.end())
|
|
{
|
|
vtkErrorMacro("Cannot find command function for \"" << cname << "\".");
|
|
return 1;
|
|
}
|
|
|
|
const vtkClientServerInterpreterInternals::CommandFunction* n = f->second;
|
|
|
|
vtkClientServerCommandFunction function = n->Function;
|
|
void* ctx = n->Context ? n->Context->Context : 0;
|
|
|
|
return function(this, ptr, method, msg, result, ctx);
|
|
}
|
|
|
|
void
|
|
vtkClientServerInterpreter::AddNewInstanceFunction(const char* name,
|
|
vtkClientServerNewInstanceFunction f,
|
|
void* ctx,
|
|
vtkContextFreeFunction freeFunction)
|
|
{
|
|
vtkClientServerInterpreterInternals::NewInstanceFunctionsType::const_iterator it=
|
|
this->Internal->NewInstanceFunctions.find(name);
|
|
|
|
if(it != this->Internal->NewInstanceFunctions.end())
|
|
{
|
|
if(it->second->Context == ctx && it->second->Function == f)
|
|
{ // we already have this EXACT definition available so don't add
|
|
return;
|
|
}
|
|
vtkErrorMacro("Ignoring duplicate instance function for " << name);
|
|
return;
|
|
}
|
|
|
|
vtkClientServerInterpreterInternals::ContextInformation* context = NULL;
|
|
if(ctx || freeFunction)
|
|
{
|
|
context = new vtkClientServerInterpreterInternals::ContextInformation(ctx, freeFunction);
|
|
}
|
|
|
|
this->Internal->NewInstanceFunctions[name] =
|
|
new vtkClientServerInterpreterInternals::NewInstanceFunction(f, context);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::Load(const char* moduleName)
|
|
{
|
|
int ret = LoadImpl(moduleName);
|
|
|
|
if (ret)
|
|
{
|
|
ret = this->Load(moduleName, 0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static
|
|
void vtkClientServerInterpreterSplit(const char* path,
|
|
char split, char slash,
|
|
std::vector<std::string>& paths)
|
|
{
|
|
std::string str = path?path:"";
|
|
std::string::size_type lpos = 0;
|
|
std::string::size_type rpos = str.npos;
|
|
while((rpos = str.find(split, lpos)) != str.npos)
|
|
{
|
|
if(lpos < rpos)
|
|
{
|
|
std::string dir = str.substr(lpos, rpos-lpos);
|
|
if(*(dir.end()-1) != slash)
|
|
{
|
|
dir += slash;
|
|
}
|
|
paths.push_back(dir);
|
|
}
|
|
lpos = rpos+1;
|
|
}
|
|
if(lpos < str.length())
|
|
{
|
|
std::string dir = str.substr(lpos);
|
|
if(*(dir.end()-1) != slash)
|
|
{
|
|
dir += slash;
|
|
}
|
|
paths.push_back(dir);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::Load(const char* moduleName,
|
|
const char*const* optionalPaths)
|
|
{
|
|
// The library search path.
|
|
typedef std::vector<std::string> PathsType;
|
|
PathsType paths;
|
|
|
|
// Try user-specified paths if any.
|
|
if(optionalPaths)
|
|
{
|
|
for(const char*const* p = optionalPaths; *p; ++p)
|
|
{
|
|
std::string path = *p;
|
|
if(path.length() > 0)
|
|
{
|
|
char end = *(path.end()-1);
|
|
if(end != '/' && end != '\\')
|
|
{
|
|
path += "/";
|
|
}
|
|
paths.push_back(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try system paths.
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
vtkClientServerInterpreterSplit(getenv("PATH"), ';', '\\', paths);
|
|
#else
|
|
vtkClientServerInterpreterSplit(getenv("LD_LIBRARY_PATH"), ':', '/', paths);
|
|
vtkClientServerInterpreterSplit(getenv("PATH"), ':', '/', paths);
|
|
paths.push_back("/usr/lib/");
|
|
paths.push_back("/usr/lib/vtk/");
|
|
paths.push_back("/usr/local/lib/");
|
|
paths.push_back("/usr/local/lib/vtk/");
|
|
#endif
|
|
|
|
// Search for the module.
|
|
std::string searched;
|
|
std::string libName = vtkDynamicLoader::LibPrefix();
|
|
libName += moduleName;
|
|
libName += vtkDynamicLoader::LibExtension();
|
|
for(PathsType::iterator p = paths.begin(); p != paths.end(); ++p)
|
|
{
|
|
struct stat data;
|
|
std::string fullPath = *p;
|
|
|
|
#if defined(CMAKE_INTDIR)
|
|
// Look in the subdirectory for the configuration in which this
|
|
// interpreter was built.
|
|
std::string fullPathWithIntDir = fullPath;
|
|
fullPathWithIntDir += CMAKE_INTDIR "/";
|
|
fullPathWithIntDir += libName;
|
|
if(stat(fullPathWithIntDir.c_str(), &data) == 0)
|
|
{
|
|
return this->LoadInternal(moduleName, fullPathWithIntDir.c_str());
|
|
}
|
|
searched += p->substr(0, p->length()-1);
|
|
searched += "/" CMAKE_INTDIR;
|
|
searched += "\n";
|
|
#endif
|
|
|
|
// Look in the directory specified.
|
|
fullPath += libName;
|
|
if(stat(fullPath.c_str(), &data) == 0)
|
|
{
|
|
return this->LoadInternal(moduleName, fullPath.c_str());
|
|
}
|
|
searched += p->substr(0, p->length()-1);
|
|
searched += "\n";
|
|
}
|
|
|
|
vtkErrorMacro("Cannot find module \"" << libName.c_str() << "\". "
|
|
<< "The following paths were searched:\n"
|
|
<< searched.c_str());
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkClientServerInterpreter::LoadInternal(const char* moduleName,
|
|
const char* fullPath)
|
|
{
|
|
// The type of a module initializer function.
|
|
typedef void (*InitFunction)(vtkClientServerInterpreter*);
|
|
|
|
// Load the library dynamically.
|
|
vtkLibHandle lib = vtkDynamicLoader::OpenLibrary(fullPath);
|
|
if(!lib)
|
|
{
|
|
vtkErrorMacro("Cannot load module \"" << moduleName << "\" from \""
|
|
<< fullPath << "\".");
|
|
const char* error = vtkDynamicLoader::LastError();
|
|
if (error)
|
|
{
|
|
vtkErrorMacro(<<error);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Get the init function.
|
|
std::string initFuncName = moduleName;
|
|
initFuncName += "_Initialize";
|
|
void* pfunc = vtkDynamicLoader::GetSymbolAddress(lib, initFuncName.c_str());
|
|
InitFunction func = *reinterpret_cast<InitFunction*>(&pfunc);
|
|
if(!func)
|
|
{
|
|
vtkErrorMacro("Cannot find function \"" << initFuncName.c_str()
|
|
<< "\" in \"" << fullPath << "\".");
|
|
return 0;
|
|
}
|
|
|
|
// Call the init function.
|
|
func(this);
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkClientServerInterpreter::ClearLastResult()
|
|
{
|
|
this->LastResultMessage->Reset();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
vtkClientServerID vtkClientServerInterpreter::GetNextAvailableId()
|
|
{
|
|
return vtkClientServerID(++this->NextAvailableId);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkObjectBase* vtkClientServerInterpreter::NewInstance(const char* classname)
|
|
{
|
|
if(!this->Internal->NewInstanceFunctions.count(classname))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
const vtkClientServerInterpreterInternals::NewInstanceFunction* n =
|
|
this->Internal->NewInstanceFunctions[classname];
|
|
|
|
vtkClientServerNewInstanceFunction function = n->Function;
|
|
void* ctx = n->Context ? n->Context->Context : 0;
|
|
|
|
return function(ctx);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkClientServerInterpreter::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
}
|