File: [Development] / inventor / lib / database / src / so / SoTranscribe.c++ (download)
Revision 1.1.1.1 (vendor branch), Tue Aug 15 12:56:17 2000 UTC (17 years, 2 months ago) by naaman
Branch: sgi, MAIN
CVS Tags: start, release-2_1_5-9, release-2_1_5-8, release-2_1_5-10, HEAD Changes since 1.1: +0 -0
lines
Initial check-in based on 2.1.5 (SGI IRIX) source tree.
|
/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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 GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1990,91 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.1.1.1 $
|
| Classes:
| SoTranSender, SoTranReceiver
|
| Author(s) : Paul S. Strauss
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/SoOutput.h>
#include <Inventor/misc/SoTranscribe.h>
#include <Inventor/actions/SoWriteAction.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/fields/SoFieldData.h>
#include <Inventor/nodes/SoGroup.h>
// Internal command codes
#define TRAN_INSERT 0
#define TRAN_INSERTP 1
#define TRAN_REMOVE 2
#define TRAN_REPLACE 3
#define TRAN_MODIFY 4
#define TRAN_END 5
#define TRAN_NUM 6 /* Number of transcription commands */
//////////////////////////////////////////////////////////////////////////////
//
// SoTranSender class
//
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor - takes SoOutput to write transcription to.
//
// Use: public
SoTranSender::SoTranSender(SoOutput *output)
//
////////////////////////////////////////////////////////////////////////
{
out = output;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds INSERT command to output.
//
// Use: public
void
SoTranSender::insert(SoNode *node)
//
////////////////////////////////////////////////////////////////////////
{
node->ref();
addCommand(TRAN_INSERT);
addNode(node);
node->unref();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds INSERTP command to output.
//
// Use: public
void
SoTranSender::insert(SoNode *node, SoNode *parent, int n)
//
////////////////////////////////////////////////////////////////////////
{
node->ref();
if (parent != NULL)
parent->ref();
addCommand(TRAN_INSERTP);
addNode(node);
addNodeRef(parent);
addInt(n);
node->unref();
if (parent != NULL)
parent->unref();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds REMOVE command to output.
//
// Use: public
void
SoTranSender::remove(SoNode *parent, int n)
//
////////////////////////////////////////////////////////////////////////
{
if (parent != NULL)
parent->ref();
addCommand(TRAN_REMOVE);
addNodeRef(parent);
addInt(n);
if (parent != NULL)
parent->unref();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds REPLACE command to output.
//
// Use: public
void
SoTranSender::replace(SoNode *parent, int n, SoNode *newNode)
//
////////////////////////////////////////////////////////////////////////
{
if (parent != NULL)
parent->ref();
newNode->ref();
addCommand(TRAN_REPLACE);
addNodeRef(parent);
addInt(n);
addNode(newNode);
if (parent != NULL)
parent->unref();
newNode->unref();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds MODIFY command to output.
//
// Use: public
void
SoTranSender::modify(SoNode *node)
//
////////////////////////////////////////////////////////////////////////
{
node->ref();
addCommand(TRAN_MODIFY);
addNodeRef(node);
addNode(node, FALSE);
node->unref();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This is called to make sure the data is ready to send. It adds
// an END command to the output.
//
// Use: public
void
SoTranSender::prepareToSend()
//
////////////////////////////////////////////////////////////////////////
{
addCommand(TRAN_END);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds string corresponding to command code to buffer.
//
// Use: private
void
SoTranSender::addCommand(int command)
//
////////////////////////////////////////////////////////////////////////
{
out->write(command);
if (! out->isBinary())
out->write('\n');
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds data for node to output. Uses Inventor database write to do
// the deed. If addNames is TRUE (the default), also adds names.
//
// Use: private
void
SoTranSender::addNode(SoNode *node, SbBool addNames)
//
////////////////////////////////////////////////////////////////////////
{
SoWriteAction wa(out);
wa.apply(node);
// Add a list of node "names" in order
if (addNames)
addNodeNames(node);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds a reference to the given node to the output. The reference
// is written as a string containing the address of the node. The
// actual address doesn't matter - just that all nodes have unique
// references.
//
// Use: private
void
SoTranSender::addNodeRef(const SoNode *node)
//
////////////////////////////////////////////////////////////////////////
{
// Convert the node pointer to a string
char s[16];
sprintf(s, "%#x", node);
out->write(s);
if (! out->isBinary())
out->write('\n');
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds an integer to the buffer.
//
// Use: private
void
SoTranSender::addInt(int n)
//
////////////////////////////////////////////////////////////////////////
{
out->write(n);
if (! out->isBinary())
out->write('\n');
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds a dictionary of references for all nodes under the given
// root to the output. This traverses the graph in the usual style;
// the receiver must traverse in the same manner to keep references
// correct.
//
// Use: private
void
SoTranSender::addNodeNames(const SoNode *root)
//
////////////////////////////////////////////////////////////////////////
{
addNodeRef(root);
if (root->isOfType(SoGroup::getClassTypeId())) {
const SoGroup *group = (const SoGroup *) root;
int i;
for (i = 0; i < group->getNumChildren(); i++)
addNodeNames(group->getChild(i));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// SoTranReceiver class
//
//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//
// This structure is added to the nameToEntryDict dictionary that
// correlates unique node names to node pointers. The unique names are
// created by printing the pointer to the transcribed node into a
// string buffer. This structure stores a pointer to the node on the
// receiving end. It also stores a reference count for that node so we
// can tell how many times it has been added to the dictionary. When
// this gets decremented to 0, the dictionary entry is removed, so we
// can free up some memory. Without this, transcription just uses up
// memory because new nodes keep getting inserted and none get removed
// from the dictionary.
//
///////////////////////////////////////////////////////
struct SoTranDictEntry {
SoNode *node; // Pointer to node
int32_t refCount; // Number of times node is added to dict
};
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor - takes default root node.
//
// Use: public
SoTranReceiver::SoTranReceiver(SoGroup *rootNode)
//
////////////////////////////////////////////////////////////////////////
{
char s[16];
// Add root to dictionaries with name for NULL, since that is how
// the root is referred to.
sprintf(s, "%#x", NULL);
SbName name(s);
addEntry(rootNode, name);
root = rootNode;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor
//
// Use: public
SoTranReceiver::~SoTranReceiver()
//
////////////////////////////////////////////////////////////////////////
{
// Free up SoTranDictEntry instances stored in dictionary
nameToEntryDict.applyToAll(SoTranReceiver::deleteDictEntry);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Interprets transcription commands from the given SoInput.
// Returns FALSE on error.
//
// Use: public
SbBool
SoTranReceiver::interpret(SoInput *in)
//
////////////////////////////////////////////////////////////////////////
{
int commandCode;
SbBool done = FALSE;
// Run through commands in buffer, changing local DB
do {
if (! in->read(commandCode))
return FALSE;
// Process command
if (! interpretCommand(commandCode, in, done)) {
SoDebugError::post("SoTranReceiver::interpret",
"in command \"%d\"", commandCode);
return FALSE;
}
} while (! done);
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Interprets one database change command (with given code) from
// input. Sets done to TRUE if end command was found. Returns T/F
// error status.
//
// Use: private
SbBool
SoTranReceiver::interpretCommand(int commandCode, SoInput *in, SbBool &done)
//
////////////////////////////////////////////////////////////////////////
{
SoNode *node, *parentNode, *nodeRef;
SoGroup *parent;
const SoFieldData *fd;
int i;
switch (commandCode) {
case TRAN_INSERT:
if (! getNodeAndNames(in, node)) // New node and names
return FALSE;
root->addChild(node);
node->unref();
break;
case TRAN_INSERTP:
if (! getNodeAndNames(in, node) || // New node and names
! getNodeReference(in, parentNode) || // Parent node
! in->read(i)) // Child index
return FALSE;
parent = (SoGroup *) parentNode;
parent->insertChild(node, i);
node->unref();
break;
case TRAN_REMOVE:
if (! getNodeReference(in, parentNode) || // Parent of node
! in->read(i)) // Child index
return FALSE;
parent = (SoGroup *) parentNode;
if (i < parent->getNumChildren()) {
// Remove references, recursively
removeNodeReferences(parent->getChild(i));
parent->removeChild(i);
}
break;
case TRAN_REPLACE:
if (! getNodeReference(in, parentNode) || // Parent of node
! in->read(i) || // Child index
! getNodeAndNames(in, node)) // New node and names
return FALSE;
// Out with the old, in with the new
parent = (SoGroup *) parentNode;
if (i < parent->getNumChildren()) {
// Remove references, recursively
removeNodeReferences(parent->getChild(i));
parent->removeChild(i);
parent->insertChild(node, i);
node->unref();
}
break;
case TRAN_MODIFY:
if (! getNodeReference(in, nodeRef) || // Reference to node
! getNode(in, node)) // New node definition
return FALSE;
// Change node fields to new contents
fd = nodeRef->getFieldData();
if (fd != NULL)
fd->overlay(nodeRef, node, TRUE);
// Get rid of new definition
node->unref();
break;
case TRAN_END:
done = TRUE;
break;
default:
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a node (and its childrem, if any) from the buffer,
// returning the node. This also reads the node names data from the
// buffer, updating the node dictionaries. Returns FALSE on error.
//
// Use: private
SbBool
SoTranReceiver::getNodeAndNames(SoInput *in, SoNode *&node)
//
////////////////////////////////////////////////////////////////////////
{
SoNode *oldNode;
// Read node
if (! getNode(in, node))
return FALSE;
// Read node names, looking for existing node with that name
if (! getNodeNames(in, node, TRUE, oldNode))
return FALSE;
// If node already existed, get rid of new stuff and return old node
if (oldNode != NULL) {
node->unref();
node = oldNode;
node->ref();
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a node (and its childrem, if any) from the buffer,
// returning the node (after ref'ing it). Returns FALSE on error.
//
// Use: private
SbBool
SoTranReceiver::getNode(SoInput *in, SoNode *&node)
//
////////////////////////////////////////////////////////////////////////
{
if (! SoDB::read(in, node))
return FALSE;
node->ref();
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads reference names for each node under root. Assumes that the
// order of traversal is the same here as when sending the name. If
// lookForNode is TRUE, it looks for the node in the dictionary. If
// it is found, oldRoot is set to point to it. Otherwise, oldRoot
// is set to NULL. If lookForNode is FALSE, this just recurses on
// the children, if any, to keep the name-to-node correspondences
// correct.
//
// Use: private
SbBool
SoTranReceiver::getNodeNames(SoInput *in, SoNode *root,
SbBool lookForNode, SoNode *&oldRoot)
//
////////////////////////////////////////////////////////////////////////
{
SbName name;
void *ptr;
SoTranDictEntry *e;
// Read reference as SbName to get unique pointer
if (! in->read(name))
return FALSE;
if (lookForNode) {
// See if the node is already in the dictionary. If so,
// increment the reference count and return the old root.
if (nameToEntryDict.find((unsigned long) name.getString(), ptr)) {
e = (SoTranDictEntry *) ptr;
e->refCount++;
oldRoot = e->node;
}
// Otherwise, add a new entry
else {
addEntry(root, name);
oldRoot = NULL;
}
}
else
oldRoot = NULL;
if (root->isOfType(SoGroup::getClassTypeId())) {
SoNode *child;
SoGroup *group = (SoGroup *) root;
int i;
for (i = 0; i < group->getNumChildren(); i++) {
// Recurse
if (! getNodeNames(in, group->getChild(i), oldRoot == NULL, child))
return FALSE;
// If child node was already in dictionary, replace child
// of group with node from dictionary.
if (child != NULL)
group->replaceChild(i, child);
}
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a node reference from the input and returns (in node) a
// pointer to the node referenced by it. If no such node is found,
// NULL is returned. Returns FALSE on error.
//
// Use: private
SbBool
SoTranReceiver::getNodeReference(SoInput *in, SoNode *&node)
//
////////////////////////////////////////////////////////////////////////
{
SbName name;
void *ptr;
// Read reference as SbName to get unique pointer
if (! in->read(name))
return FALSE;
// Look up pointer in dictionary
if (nameToEntryDict.find((unsigned long) name.getString(), ptr))
node = ((SoTranDictEntry *) ptr)->node;
else {
node = NULL;
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Removes reference to node in dictionaries. The passed node is a
// node in the graph on the receiving side. If there are no more
// references left, removes dictionary entry completely.
// (Otherwise, just decrements dictionary entry reference count.)
// This is a recursive procedure.
//
// Use: private
void
SoTranReceiver::removeNodeReferences(SoNode *node)
//
////////////////////////////////////////////////////////////////////////
{
void *ptr;
const char *nameString;
SoTranDictEntry *e;
// Find the name of the node, using the name dictionary
nodeToNameDict.find((unsigned long) node, ptr);
nameString = (const char *) ptr;
// Find the dictionary entry for the node, using the name
SbName name(nameString);
nameToEntryDict.find((unsigned long) name.getString(), ptr);
e = (SoTranDictEntry *) ptr;
// Decrement the reference count and check for 0
if (--e->refCount == 0) {
// This node is going away. Remove its dictionary entries and
// free up the SoTranDictEntry
nameToEntryDict.remove((unsigned long) name.getString());
nodeToNameDict.remove((unsigned long) node);
e->node->unref();
delete e;
// Recurse on its children, if it has any
if (node->isOfType(SoGroup::getClassTypeId())) {
const SoGroup *group = (const SoGroup *) node;
int i;
for (i = 0; i < group->getNumChildren(); i++)
removeNodeReferences(group->getChild(i));
}
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds an entry to the dictionaries.
//
// Use: private
void
SoTranReceiver::addEntry(SoNode *node, SbName &name)
//
////////////////////////////////////////////////////////////////////////
{
SoTranDictEntry *e = new SoTranDictEntry;
e->node = node;
e->node->ref();
e->refCount = 1;
nameToEntryDict.enter((unsigned long) name.getString(), (void *) e);
nodeToNameDict.enter((unsigned long) node, (void *) name.getString());
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Deletes (frees up) an entry from the nameToEntryDict.
//
// Use: private
void
SoTranReceiver::deleteDictEntry(unsigned long, void *value)
//
////////////////////////////////////////////////////////////////////////
{
SoTranDictEntry *e = (SoTranDictEntry *) value;
e->node->unref();
delete e;
}