[BACK]Return to SoBase.c++ CVS log [TXT][DIR] Up to [Development] / inventor / lib / database / src / so

File: [Development] / inventor / lib / database / src / so / SoBase.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,92   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:
 |	SoBase
 |
 |   Author(s)		: Paul S. Strauss, Nick Thompson, Gavin Bell
 |
 ______________  S I L I C O N   G R A P H I C S   I N C .  ____________
 _______________________________________________________________________
 */

#include <dlfcn.h>
#include <ctype.h>

#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/SoOutput.h>
#include <Inventor/SoPath.h>
#include <Inventor/sensors/SoDataSensor.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/errors/SoReadError.h>
#include <Inventor/fields/SoField.h>
#include <Inventor/misc/SoBase.h>
#include <Inventor/misc/SoNotification.h>
#include <Inventor/misc/upgraders/SoUpgrader.h>
#include <SoUnknownNode.h>
#include <SoUnknownEngine.h>
#include "fields/SoGlobalField.h"

// The global name dictionaries
SbDict		*SoBase::nameObjDict;
SbDict		*SoBase::objNameDict;

// Syntax for writing instances to files
#define OPEN_BRACE		'{'
#define CLOSE_BRACE		'}'
#define DEFINITION_KEYWORD	"DEF"
#define REFERENCE_KEYWORD	"USE"
#define NULL_KEYWORD		"NULL"

SbString	SoBase::instancePrefix = "+";

SbBool		SoBase::traceRefs = FALSE;

SoType		SoBase::classTypeId;

uint32_t	SoBase::currentWriteCounter = 0;

// This speed up reading a little:
static SbName *globalFieldName;

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Setup SoBase's type info, and the global name dictionary.
//
// Use: public

void
SoBase::initClass()
//
////////////////////////////////////////////////////////////////////////
{
    classTypeId = SoType::createType(SoType::badType(), "Base");

    // Set up global name dictionaries
    nameObjDict = new SbDict;
    objNameDict = new SbDict;

    globalFieldName = new SbName("GlobalField");
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Constructor for SoBase.
//
// Use: protected

SoBase::SoBase()
//
////////////////////////////////////////////////////////////////////////
{
    refCount = 0;

    writeStuff.hasName = 0;
    writeStuff.writeCounter = 0;
    writeStuff.writeRefFromField = FALSE;

#ifdef DEBUG
    if (traceRefs)
	SoDebugError::postInfo("SoBase::SoBase",
			       "for %#x", (const void *) this);
#endif /* DEBUG */
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Destructor for SoBase.
//
// Use: protected

SoBase::~SoBase()
//
////////////////////////////////////////////////////////////////////////
{
    const SbName &myName = getName();
    if (myName != "")
	removeName(this, myName.getString());

#ifdef DEBUG
    if (traceRefs)
	SoDebugError::postInfo("SoBase::~SoBase",
			       "for %#x", (const void *) this);
#endif /* DEBUG */
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Adds a reference to an instance. This is defined to be const so
//    users can call it on any instance, even const ones.
//
// Use: public

void
SoBase::ref() const
//
////////////////////////////////////////////////////////////////////////
{
    // This generates a C++ warning.
    ((SoBase *) this)->refCount++;

#ifdef DEBUG
    if (traceRefs)
	SoDebugError::postInfo("SoBase::ref",
			       "refCount for %#x + => %2d",
			       (const void *) this, refCount);
#endif /* DEBUG */
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Removes a reference to an instance, deleting it if count becomes 0.
//    This is defined to be const so users can call it on any
//    instance, even const ones.
//
// Use: public

void
SoBase::unref() const
//
////////////////////////////////////////////////////////////////////////
{
    // This generates a C++ warning.
    SoBase 	*base = (SoBase *) this;

#ifdef DEBUG
    if (base->refCount <= 0)
	SoDebugError::postWarning("SoBase::unref",
				  "instance has reference count <= 0 already");

    if (traceRefs)
	SoDebugError::postInfo("SoBase::unref",
			       "refCount for %#x - => %2d",
			       (const void *) this, refCount - 1);
#endif /* DEBUG */

    if (--base->refCount == 0)
	// This generates a C++ warning
	((SoBase *) this)->destroy();
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Removes a reference to an instance, NOT deleting it if count becomes 0.
//    Used with ref to temporarily make something safe for some other operation,
//    then restore the object to exactly the reference-count-state that it was.
//
// Use: public

void
SoBase::unrefNoDelete() const
//
////////////////////////////////////////////////////////////////////////
{
    // This generates a C++ warning.
    SoBase 	*base = (SoBase *) this;

#ifdef DEBUG
    if (base->refCount <= 0)
	SoDebugError::postWarning("SoBase::unrefNoDelete",
				  "instance has reference count <= 0 already");

    if (traceRefs)
	SoDebugError::postInfo("SoBase::unrefNoDelete",
			       "refCount for %#x - => %2d",
			       (const void *) this, refCount - 1);
#endif /* DEBUG */
    base->refCount--;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Returns TRUE if base is an instance of a node of the given type
//    or an instance of a subclass of it.
//
// Use: public

SbBool					// Returns TRUE or FALSE
SoBase::isOfType(SoType type) const	// Type inquiring about
//
////////////////////////////////////////////////////////////////////////
{
    return getTypeId().isDerivedFrom(type);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Returns the name of an instance.  If the instance has no name,
//    SbName("") will be returned.
//
// Use: public

SbName
SoBase::getName() const
//
////////////////////////////////////////////////////////////////////////
{
    void *n;

    if (!writeStuff.hasName) return SbName("");
    if (!objNameDict->find((unsigned long)this, n)) {
#ifdef DEBUG
	SoDebugError::post("SoBase::getName",
			   "hasName is TRUE, but couldn't find name!\n");
#endif
	return SbName("");
    }
    return SbName((char *)n);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Sets an instance's name.  Setting the name to "" un-names the
//    object.  The global dictionary stored in SoDB is updated based
//    on whatever is passed.
//
// Use: public

void
SoBase::setName(const SbName &newName)
//
////////////////////////////////////////////////////////////////////////
{
    //Following 4 lines just do what getName() would do, repeating that code
    //here fixes a thread-safe bug
    void *n;
    SbName oldName("");
    if ((writeStuff.hasName) && (objNameDict->find((unsigned long)this, n)))
	oldName = SbName((char *)n); 
    
    if (oldName.getLength() != 0)
	removeName(this, oldName.getString());

    // Empty name: leave unnamed.
    if (newName.getLength() == 0) return;

    // Make sure name is legal
    const char *str = newName.getString();
    SbBool isBad = 0;

    // Check for beginning-with-a-number:
    if (!SbName::isBaseNameStartChar(str[0])) isBad = TRUE;

    int i;
    for (i = 1; i < newName.getLength() && !isBad; i++) {
	isBad = !SbName::isBaseNameChar(str[i]);
    }

    if (!isBad) {
	addName(this, str);
    }
    else {
	// Replace bad characters with underscores
	SbString goodString;

	// Prepend underscore if name begins with number:
	if (!SbName::isBaseNameStartChar(str[0])) {
	    goodString += "_";
	}
	for (i = 0; i < newName.getLength(); i++) {
	    // Ugly little hack so we can use SbString's += operator,
	    // which doesn't do char's (only char *'s):
	    char temp[2];
	    temp[0] = str[i]; temp[1] = '\0';
	    if (!SbName::isBaseNameChar(str[i]))
		goodString += "_";
	    else
		goodString += temp;
	}
#ifdef DEBUG
	SoDebugError::post("SoBase::setName", "Bad characters in"
			   " name '%s'.  Replacing with name '%s'",
			   str, goodString.getString());
#endif       
	// MUST create an SbName here to create persistent storage for
	// the name.
	SbName goodName(goodString.getString());
	addName(this, goodName.getString());
    }
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Actually deletes an instance. Allows subclasses to do other
//    stuff before the deletion if necessary.
//
// Use: protected

void
SoBase::destroy()
//
////////////////////////////////////////////////////////////////////////
{
    // If there are any auditors left, give them a chance to clean up.
    // The only type of auditors that can be attached to a base with a
    // zero ref count (which is why we are here) are sensors, which
    // are given a chance to detach themselves.
    //
    // NOTE: The implementation of dyingReference() for sensors may
    // require this base instance to be valid. Therefore, we cannot do
    // this stuff in the destructor, or it would be too late.

    for (int i = auditors.getLength() - 1; i >= 0; i--) {

	switch (auditors.getType(i)) {
	  case SoNotRec::SENSOR:
	    // Tell sensor that we are going away
	    ((SoDataSensor *) auditors.getObject(i))->dyingReference();

	    // The call to dyingReference() might remove auditors,
	    // shortening the auditors list; make sure we're not
	    // trying to access past the end.
	    if (i > auditors.getLength())
		i = auditors.getLength();
	    break;

	  default:
	    SoDebugError::post("(internal) SoBase::destroy",
			       "Got an auditor of type %d",
			       (int) auditors.getType(i));
	    break;
	}
    }

    delete this;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Writes a header (name, open brace) to file defined by SoOutput.
//    If the instance is multiply-referenced for writing, this will
//    either write a definition or reference to the instance. If
//    it writes out a reference, this returns TRUE, meaning that no
//    further writing is necessary for the instance.
//
// Use: protected

SbBool
SoBase::writeHeader(SoOutput *out, SbBool isGroup, SbBool isEngine) const
//
////////////////////////////////////////////////////////////////////////
{
    SbBool	isBinary = out->isBinary();

    // Cast const away
    if (! ((SoBase *) this)->shouldWrite())
	return TRUE;

    if (! isBinary)
	out->indent();

    // If this instance is named, but not multiply referenced, write
    // out the object's name
    if (getName().getLength() != 0 && ! hasMultipleWriteRefs()) {
	// We don't need to bother adding this name to the output
	// dictionary, since this isn't instanced.
	writeDef(out, -1);
    }
    // If multiply-referenced, check the dictionary for a previous definition
    else if (hasMultipleWriteRefs()) {
	int referenceId = out->findReference(this);

	// If already defined, write reference
	if (referenceId != (-1)) {
	    writeRef(out, referenceId);
	    return TRUE;
	}

	// Otherwise, add to dictionary and write definition
	else
	    writeDef(out, out->addReference(this));
    }

    // Write class name
    out->write(getFileFormatName());

    if (! isBinary) {
	out->write(' ');
	out->write(OPEN_BRACE);

	writeAnnotation(out);

	// Prepare for writing insides
	out->incrementIndent();
    } else {
	unsigned short ioFlags = 0x0;
	if (isEngine)
	    ioFlags |= IS_ENGINE;
	if (isGroup)
	    ioFlags |= IS_GROUP;
	out->write(ioFlags);
    }

    return FALSE;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Writes a footer (close brace) to file defined by SoOutput.
//
// Use: protected

void
SoBase::writeFooter(SoOutput *out) const
//
////////////////////////////////////////////////////////////////////////
{
    if (! out->isBinary()) {

	// Done writing insides
	out->decrementIndent();

	// Write footer
	out->indent();
	out->write(CLOSE_BRACE);
	out->write('\n');
    }
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Returns file format name.  Overriden by unknown node/engine
//
// Use: protected

const char *
SoBase::getFileFormatName() const
//
////////////////////////////////////////////////////////////////////////
{
    return getTypeId().getName().getString();
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Initiates notification from an instance. The default method sets
//    up a new notification list and propagates it to all auditors.
//
// Use: internal

void
SoBase::startNotify()
//
////////////////////////////////////////////////////////////////////////
{
    SoNotRec	rec(this);
    SoNotList	list;

    // Indicate to the database that a notification is in progress
    SoDB::startNotify();

    // Assume the notification type is CONTAINER. This is a safe bet
    // for now. If it is another type, it will be changed
    // appropriately later on. We need to set it to something so that
    // SoFieldContainer will have some deterministic way to act.
    rec.setType(SoNotRec::CONTAINER);

    list.append(&rec);
    notify(&list);

    // Indicate to the database that the notification has completed
    SoDB::endNotify();
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Propagates notification of modification through an instance of
//    some subclass of SoBase. The default method here does not create
//    and add a new record. It merely propagates the current record
//    list to all auditors. This method may be used by subclasses to
//    do the propagation after modifying the list appropriately.
//
// Use: internal

void
SoBase::notify(SoNotList *list)
//
////////////////////////////////////////////////////////////////////////
{
    auditors.notify(list);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Adds an auditor to list of auditors.
//
// Use: private

void
SoBase::addAuditor(void *auditor, SoNotRec::Type type)
//
////////////////////////////////////////////////////////////////////////
{
    auditors.append(auditor, type);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Removes an auditor from list of auditors.
//
// Use: private

void
SoBase::removeAuditor(void *auditor, SoNotRec::Type type)
//
////////////////////////////////////////////////////////////////////////
{
    int	audIndex = auditors.find(auditor, type);

#ifdef DEBUG
    if (audIndex < 0) {
	SoDebugError::post("SoBase::removeAuditor",
			   "can't find auditor %#x\n", auditor);
	return;
    }
#endif /* DEBUG */

    auditors.remove(audIndex);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Add a name/instance pair to the global dictionaries.  The
//    nameObjDict is keyed by the SbName's string's address.  The
//    value in each dictionary slot is a pointer to a SbPList
//    containing all SoBase's with the given name (the lists are
//    created dynamically when needed).  Even though the list stores
//    only SoBases, an SoBaseList isn't used because we don't want the
//    items on the list to be reference counted (if they are, they
//    will never be deleted if they are named).
//    The objNameDict is keyed by the SoBase's name.
//
// Use: internal

void
SoBase::addName(SoBase *b, const char *name)
//
////////////////////////////////////////////////////////////////////////
{
    SbPList *list;
    void *t;

    b->writeStuff.hasName = 1;

    // Look for name:
    if (!nameObjDict->find((unsigned long)name, t)) {
	// If not found, create a BaseList and enter it in the
	// dictionary
	list = new SbPList;
	nameObjDict->enter((unsigned long)name, list);
    } else {
	list = (SbPList *)t;
    }

#ifdef DEBUG
    // Make sure it isn't already on the list
    if (list->find(b) != -1)
	SoDebugError::post("SoBase::addName",
			   "Base %x with name \"%s\" is already in dictionary",
			   b, name);
#endif

    // Add name to the list:
    list->append(b);

    // And append to the objName dictionary:
    objNameDict->enter((unsigned long)b, (void *)name);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Removes a name/instance pair from the global dictionary.
//
// Use: internal

void
SoBase::removeName(SoBase *b, const char *name)
//
////////////////////////////////////////////////////////////////////////
{
    SbPList	*list;
    SbBool	found;
    void	*t;
    int		i;

    b->writeStuff.hasName = 0;

    // Look for name list
    found = nameObjDict->find((unsigned long) name, t);

    // Look for name within list
    if (found) {
	list = (SbPList *) t;
	i    = list->find(b);

	if (i < 0)
	    found = FALSE;

	else
	    list->remove(i);
    }

    // And remove from objName dict:
    found |= objNameDict->remove((unsigned long)b);

#ifdef DEBUG
    if (! found)
	SoDebugError::post("SoBase::removeName",
			   "Name \"%s\" (base %x) is not in dictionary",
			   name, b);
#endif

    return;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Internal routines used by node/path/function getByName routines.
//
// Use: internal, static

SoBase *
SoBase::getNamedBase(const SbName &name, SoType type)
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
    if (nameObjDict == NULL) {
	SoDebugError::post("SoBase::getName",
			   "SoDBinit() has not yet been called");
	return NULL;
    }
#endif

    SbPList *list;
    void *t;
    // Lookup the name in the dictionary
    if (! nameObjDict->find((unsigned long) name.getString(), t))
	return NULL;
    list = (SbPList *)t;

    // Search backwards through the list.  Return the last item of the
    // appropriate type.
    for (int i = list->getLength()-1; i >= 0; i--) {
	SoBase *b = (SoBase *)(*list)[i];
	if (b->isOfType(type)) return b;
    }
    return NULL;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Internal routine used by the get() routines above.  Returns the
//    number of items added to the result list.
//
// Use: internal, static

int
SoBase::getNamedBases(const SbName &name, SoBaseList &result, SoType type)
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
    if (nameObjDict == NULL) {
	SoDebugError::post("SoBase::getName",
			   "SoDBinit() has not yet been called");
	return 0;
    }
#endif

    int numAdded = 0;
    SbPList *list;

    void *t;
    // Lookup the name in the dictionary
    if (! nameObjDict->find((unsigned long) name.getString(), t))
	return 0;

    list = (SbPList *)t;
    // Search backwards through the list.  Add all items of the
    // appropriate type to the result list.
    for (int i = list->getLength()-1; i >= 0; i--) {
	SoBase *b = (SoBase *)(*list)[i];
	if (b->isOfType(type)) {
	    result.append(b);
	    numAdded++;
	}
    }
    return numAdded;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Reads one instance of some subclass of SoBase. Returns pointer
//    to read-in instance in base, or NULL on EOF. Returns FALSE on
//    error. The last parameter is a subclass type to match. If the
//    returned base is not of this type, it is an error. A type of
//    ANY_TYPE (the default) will match any base.
//
// Use: internal

SbBool
SoBase::read(SoInput *in, SoBase *&base, SoType expectedType)
//
////////////////////////////////////////////////////////////////////////
{
    SbName	name;
    SbBool	ret;

    // Read header: name and opening brace. If not found, not an error -
    // just nothing to return.
    if (! in->read(name, TRUE)) {
	base = NULL;
	ret = in->curFile->headerOk;
    }

    // If name is empty, treat this as EOF. This happens, for example,
    // when the last child of a group has been read and the '}' is next.
    else if (! name) {
	base = NULL;
	ret = TRUE;
    }

    // Check for special case of name "NULL", indicating a NULL node
    // or path pointer. (This is used for node and path fields.)
    else if (name == NULL_KEYWORD) {
	base = NULL;
	ret = TRUE;
    }

    // Check for reference to existing node/path/function
    else if (name == REFERENCE_KEYWORD)
	ret = readReference(in, base);
    else
	ret = readBase(in, name, base);

    // Check for type match
    if (base != NULL) {
	if (! base->isOfType(expectedType)) {
	    const char *baseName = base->getTypeId().getName().getString();
	    SoReadError::post(in, "Expected a %s but got a %s",
			      expectedType.getName().getString(), baseName);
	    ret = FALSE;
	}
    }

    return ret;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Adds a reference to the instance when writing. isFromField
//    indicates whether the reference is from a field-to-field
//    connection.
//
// Use: internal, virtual

void
SoBase::addWriteReference(SoOutput *, SbBool isFromField)
//
////////////////////////////////////////////////////////////////////////
{
    // If adding a reference from a field-to-field connection, just
    // set that flag to indicate it
    if (isFromField)
	writeStuff.writeRefFromField = TRUE;

    // Otherwise, update the counter if this is the first reference
    else if (writeStuff.writeCounter != getCurrentWriteCounter()) {
	writeStuff.writeCounter = getCurrentWriteCounter();
	writeStuff.multWriteRef = FALSE;
    }

    // Otherwise, indicate that we have multiple write references
    else
	writeStuff.multWriteRef = TRUE;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Returns TRUE if the instance should be written, based on the
//    write-reference info already accumulated.
//
// Use: internal

SbBool
SoBase::shouldWrite()
//
////////////////////////////////////////////////////////////////////////
{
    // If there is at least one current write reference
    if (writeStuff.writeCounter == getCurrentWriteCounter()) {

	// If there's also a reference from a field, make sure we know
	// that we have multiple references and reset the field flag
	if (writeStuff.writeRefFromField) {
	    writeStuff.multWriteRef = TRUE;
	    writeStuff.writeRefFromField = FALSE;
	}

	return TRUE;
    }

    // Ignore cases referenced only from field connections, and reset
    // the flag for next time
    if (writeStuff.writeRefFromField)
	writeStuff.writeRefFromField = FALSE;

    return FALSE;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Writes DEF identifier.  The identifier has two parts-- the
//    objects name, and the objects reference ID.  If the object is
//    not named, just '+referenceId' will be written.  If the object
//    is not instanced, then just the name will be written.  This
//    routine must not be called for objects that are neither named
//    nor instanced.
//
// Use: private

void
SoBase::writeDef(SoOutput *out, int referenceId) const
//
////////////////////////////////////////////////////////////////////////
{
    out->write(DEFINITION_KEYWORD);
    if (! out->isBinary())
	out->write(' ');

    // Assemble the whole identifier at once, so binary writing works
    // properly (it writes the identifier as count followed by the
    // characters).
    SbString t;
    const SbName &myName = getName();
    if (myName.getLength() != 0)
	t += myName.getString();
    if (referenceId != -1) {
	t += instancePrefix.getString();
	t += SbString(referenceId);
    }
    out->write(t.getString());
    if (! out->isBinary())
	out->write(' ');
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Writes USE identifier.  See the description of referenceId in
//    the writeDef function above, and note that since this is writing
//    a reference, it must always have a referenceId.
//
// Use: private

void
SoBase::writeRef(SoOutput *out, int referenceId) const
//
////////////////////////////////////////////////////////////////////////
{
    out->write(REFERENCE_KEYWORD);
    if (! out->isBinary())
	out->write(' ');

    SbString t;
    const SbName &myName = getName();
    if (myName.getLength() != 0) {
	t += myName.getString();
    }
    t += instancePrefix.getString();
    t += SbString(referenceId);
    out->write(t.getString());

    writeAnnotation(out);
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    If writing in ASCII, this will write out the address and
//    reference count of the Base for debugging purposes (if the
//    correct bits are set on the SoOutput, of course).
//
// Use: private

void
SoBase::writeAnnotation(SoOutput *out) const
//
////////////////////////////////////////////////////////////////////////
{
    if (out->isBinary()) return;
    if (out->getAnnotation()
#ifdef DEBUG
	|| traceRefs
#endif /* DEBUG */
	) {
	out->write(" #");
	if (out->getAnnotation() & SoOutput::ADDRESSES) {
	    char buf[100];
	    sprintf(buf, " %#x", this);
	    out->write(buf);
	}
	if (out->getAnnotation() & SoOutput::REF_COUNTS
#ifdef DEBUG
	    || traceRefs
#endif /* DEBUG */
	    ) {
	    out->write(" RefCount=");
	    out->write(refCount);
	}
    }
    out->write('\n');
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Reads name of referenced base instance (assumes reference
//    keyword was read) and sets base to point to it. Returns FALSE on
//    error.
//
// Use: private

SbBool
SoBase::readReference(SoInput *in, SoBase *&base)
//
////////////////////////////////////////////////////////////////////////
{
    SbName	refName;
    SbBool	ret = TRUE;

    // Get name of referenced thing
    if (! in->read(refName, FALSE)) {
	SoReadError::post(in, "Premature end of file after "
			  REFERENCE_KEYWORD);
	ret = FALSE;
    }

    // Look name up in the dictionary
    else {
	// Ok, in ASCII we might have read too much-- check for a '.'
	// in the name, meaning we read the field identifier, too, and
	// will have to re-figure the name and put back the extra
	// characters:
	if ( !in->isBinary()) {
	    const char *chars = refName.getString();
	    for (int i = 0; i < refName.getLength(); i++) {
		if (chars[i] == '.') {
		    in->putBack(chars+i);
		    refName = SbString(chars, 0, i-1);
		}
	    }
	}

	if ((base = in->findReference(refName)) == NULL) {
	    SoReadError::post(in, "Unknown reference \"%s\"",
			      refName.getString());
	    ret = FALSE;
	}
    }

    return ret;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Reads base. Sets base to point to read-in instance. Returns
//    FALSE on error.
//
// Use: private

SbBool
SoBase::readBase(SoInput *in, SbName &className, SoBase *&base)
//
////////////////////////////////////////////////////////////////////////
{
    SbBool		gotChar;
    SbName		refName;
    char		c;
    SbBool		ret = TRUE, flush = FALSE;

    // Assume NULL for now
    base = NULL;

    // Check for definition of new node/path
    if (className == DEFINITION_KEYWORD) {

	if (! in->read(refName, FALSE) || ! in->read(className, TRUE)) {
	    SoReadError::post(in, "Premature end of file after "
			      DEFINITION_KEYWORD);
	    ret = FALSE;
	}

	if (! refName) {
	    SoReadError::post(in, "No name given after ", DEFINITION_KEYWORD);
	    ret = FALSE;
	}

	if (! className) {
	    SoReadError::post(in, "Invalid definition of %s",
			      refName.getString());
	    ret = FALSE;
	}
    }

    if (ret) {

	// Save whether the file is binary in
	// case we open another file before we get to the close brace.
	SbBool isBinary = in->isBinary();

	// Look for open brace.
	if (!isBinary &&
	    (! (gotChar = in->read(c)) || c != OPEN_BRACE)) {
	    if (gotChar)
		SoReadError::post(in, "Expected '%c'; got '%c'",
				  OPEN_BRACE, c);
	    else
		SoReadError::post(in, "Expected '%c'; got EOF", OPEN_BRACE);
	    ret = FALSE;
	}

	else {
	    ret = readBaseInstance(in, className, refName, base);

	    if (! ret)
		flush = TRUE;

	    // Read closing brace.
	    else if (! isBinary &&
		     (! (gotChar = in->read(c)) || c != CLOSE_BRACE)) {
		if (gotChar)
		    SoReadError::post(in, "Expected '%c'; got '%c'",
				      CLOSE_BRACE, c);
		else
		    SoReadError::post(in, "Expected '%c'; got EOF",
				      CLOSE_BRACE);
		ret = FALSE;
	    }
	}
    }

    if (! ret && flush && ! in->isBinary())
	flushInput(in);

    return ret;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Reads instance of class with given name. Sets base to point to
//    read-in instance. Returns FALSE on error.
//
// Use: private

SbBool
SoBase::readBaseInstance(SoInput *in, const SbName &className,
			 const SbName &refName, SoBase *&base)
//
////////////////////////////////////////////////////////////////////////
{
    // This will be re-written with the correct values if binary file
    // format.  If ASCII, it shouldn't matter:
    unsigned short ioFlags = IS_GROUP | IS_ENGINE;

    SbBool isBinary = in->isBinary();
    SbBool oldFileFormat = (in->getIVVersion() < 2.1f);

    if (isBinary && !oldFileFormat) {
	in->read(ioFlags);
    }

    // Special case for global fields. Even though the word
    // "GlobalField" appears in the input file, the field itself
    // depends on the name about to be read. If the same name is used
    // twice, the same instance has to be used, so we don't want to
    // create a new instance each time.
    if (className == *globalFieldName) {
	base = SoGlobalField::read(in);

	if (base == NULL) return FALSE;
	
	// Store instance in input dictionary if a name was given for
	// it (do NOT want to add it to the global dictionary; it's
	// DEF name is not used for that).
	if (! (!refName))
	    in->addReference(refName, base, FALSE);

	return TRUE;
    }
    
    // And special case for nodes that need conversion from an old
    // version of the file format:
    SoUpgrader *upgrader;
    if ((upgrader = SoUpgrader::getUpgrader(className, in->getIVVersion()))
	!= NULL) {
	    
	upgrader->ref();
	SbBool result = upgrader->upgrade(in, refName, base);
	upgrader->unref();

	return result;
    }

    // The common case:

    // Create an instance of named type
    base = createInstance(in, className, ioFlags);
    if (base == NULL)
	return FALSE;

    // Store instance in dictionary if a name was given for it
    // This may also name the base, depending on the form of refName.
    if (! (!refName))
	in->addReference(refName, base);

    // Read stuff into instance.  Note that if the node has sensors on
    // its fields,  they might get triggered.  Make sure that the node
    // is referenced during the read.
    base->ref();
    SbBool result = base->readInstance(in, ioFlags);
    base->unrefNoDelete();

    return result;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Create instance for given className, returning a
//    pointer to it. Returns NULL on error.
//
// Use: private

SoBase *
SoBase::createInstance(SoInput *in, SbName className, unsigned short ioFlags)
//
////////////////////////////////////////////////////////////////////////
{
    SoBase		*instance = NULL;

    SbBool isBinary = in->isBinary();
    SbBool oldFileFormat = (in->getIVVersion() < 2.1f);

    // Find named type in class dictionary.
    SoType type = SoType::fromName(className);

    // Need to create an unknown node or engine:
    if (type.isBad()) {

        SbBool createEngine = FALSE;

	// ASCII, old or new file format (they're the same):
	if (!isBinary) {
	    SbString unknownString;
            SbBool  readOK = in->read(unknownString);
	    if (!readOK || ((unknownString != "fields" &&
			     unknownString != "inputs"))) {
		SoReadError::post(in, "Unknown class \"%s\"",
				  className.getString());
		return NULL;
	    }
	    in->putBack(unknownString.getString());
	    if (unknownString == "inputs") {
		createEngine = TRUE;
	    }
	}

	// Binary, new file format
	else if (!oldFileFormat && isBinary) {
	    createEngine = ioFlags & IS_ENGINE;
	}

	// Binary, old file format:
	else {
	    SbString unknownString;
            SbBool  readOK = in->read(unknownString);
	    if (!readOK || ((unknownString != "fields" &&
			     unknownString != "inputs"))) {
		SoReadError::post(in, "Unknown class \"%s\"",
				  className.getString());
		return NULL;
	    }
	    // Cannot put back the string (which is OK)
	    if (unknownString == "inputs") {
		createEngine = TRUE;
	    }
	}	    

        if (!createEngine) {
#ifdef DEBUG
	    SoDebugError::postWarning("SoBase::createInstance",
		"Creating unknown node for object of type %s "
		"(could not open DSO)", className.getString());
#endif
            SoUnknownNode *tmpNode = new SoUnknownNode();
            tmpNode->setClassName(className.getString());
	    instance = tmpNode;
	} else {
#ifdef DEBUG
	    SoDebugError::postWarning("SoBase::createInstance",
		"Creating unknown engine for object of type %s "
		"(could not open DSO)", className.getString());
#endif
            SoUnknownEngine *tmpEngine = new SoUnknownEngine;
            tmpEngine->setClassName(className.getString());
	    instance = tmpEngine;
	}
    }

    else if (!type.isDerivedFrom(SoBase::getClassTypeId())) {
	SoReadError::post(in, "\"%s\" is not an SoBase",
			  className.getString());
	instance = NULL;
    }

    else {
	instance = (SoBase *)type.createInstance();

	// We may have the name of an abstract derived class.
	if (instance == NULL) {
	    SoReadError::post(in, "class \"%s\" is an abstract class",
			      className.getString());
	}
	// Binary, old file format, not built in: read "fields" field
	else if (oldFileFormat && isBinary) {
	    if (instance->isOfType(SoFieldContainer::getClassTypeId())
	        && !((SoFieldContainer *)instance)->getIsBuiltIn()) {
		SbString unknownString;
		SbBool  readOK = in->read(unknownString);
		if (!readOK || ((unknownString != "fields" &&
				 unknownString != "inputs"))) {
		    SoReadError::post(in, "Unknown class \"%s\"",
				      className.getString());
		    return NULL;
		}
	    }
	}
    }

    return instance;
}

////////////////////////////////////////////////////////////////////////
//
// Description:
//    Flushes input up to EOF or the next closing brace (watching for
//    nested closing braces). This should be called when an error is
//    found in input after the open brace for some SoBase subclass
//    instance.
//
// Use: private

void
SoBase::flushInput(SoInput *in)
//
////////////////////////////////////////////////////////////////////////
{
    int		nestLevel = 1;
    char	c;

    while (nestLevel > 0 && in->get(c)) {

	if (c == CLOSE_BRACE)
	    nestLevel--;

	else if (c == OPEN_BRACE)
	    nestLevel++;
    }
}

#undef OPEN_BRACE
#undef CLOSE_BRACE
#undef DEFINITION_KEYWORD
#undef REFERENCE_KEYWORD
#undef NULL_KEYWORD