/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2007 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See http://www.coin3d.org/ for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * http://www.sim.no/ sales@sim.no coin-support@coin3d.org * \**************************************************************************/ /*! \class SoFieldContainer SoFieldContainer.h Inventor/fields/SoFieldContainer.h \brief The SoFieldContainer class is a base class for all classes that contain fields. \ingroup general The classes containing fields in Coin are the node and engine classes, so they are all subclasses of SoFieldContainer. SoFieldContainer provides methods for reading, writing, comparing for equality, doing copy operations, etc on fields. \sa SoField */ // ************************************************************************* #include #include #include #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #include #include #include #include #include #include #include #include #include #include #include #include #include "../io/SoWriterefCounter.h" #include // COIN_STUB() // ************************************************************************* /*! \var SbBool SoFieldContainer::isBuiltIn Flag for storing whether or not this class instance is a built-in class or not. By knowing the difference between a class which is native Coin or a user extension, it is possible to automatically store and read extension nodes and engines. */ // ************************************************************************* SoType SoFieldContainer::classTypeId STATIC_SOTYPE_INIT; // used by setUserData() and getUserData() typedef SbHash UserDataMap; static UserDataMap * sofieldcontainer_userdata_dict = NULL; static void sofieldcontainer_userdata_cleanup(void) { delete sofieldcontainer_userdata_dict; sofieldcontainer_userdata_dict = NULL; } // ************************************************************************* #define FLAG_DONOTIFY 0x01 #define FLAG_FIRSTINSTANCE 0x02 // ************************************************************************* /*! Constructor. */ SoFieldContainer::SoFieldContainer(void) : isBuiltIn(TRUE), donotify(FLAG_DONOTIFY) // HACK warning: donotify is used as a bitmask { } /*! Destructor. */ SoFieldContainer::~SoFieldContainer() { // "Dangling" userdata can cause bugs if new objects derived from // SoFieldContainer are subsequently fitted into the same memory // area that this destructed instance is placed at. So we must // remove our entry (if any -- we can just ignore the return value, // no harm is done if no data was set for this instance). (void)sofieldcontainer_userdata_dict->remove(this); } // ************************************************************************* // Use a stack of dictionaries when copying nodes to allow recursive // copying. typedef SbHash SoFieldContainerCopyMap; typedef SbHash ContentsCopiedMap; typedef struct { SbList * copiedinstancestack; SbList * contentscopiedstack; } sofieldcontainer_copydict; static void sofieldcontainer_construct_copydict(void * closure) { sofieldcontainer_copydict * data = (sofieldcontainer_copydict*) closure; data->copiedinstancestack = new SbList; data->contentscopiedstack = new SbList; } static void sofieldcontainer_destruct_copydict(void * closure) { sofieldcontainer_copydict * data = (sofieldcontainer_copydict*) closure; delete data->copiedinstancestack; delete data->contentscopiedstack; } // use thread local storage to store copydict in threadsafe version of // Coin static SbStorage * sofieldcontainer_copydictstorage; static void sofieldcontainer_copydict_cleanup(void) { delete sofieldcontainer_copydictstorage; } static sofieldcontainer_copydict * sofieldcontainer_get_copydict(void) { return (sofieldcontainer_copydict*) sofieldcontainer_copydictstorage->get(); } // ************************************************************************* // Overridden from parent class. void SoFieldContainer::initClass(void) { // Make sure we init this class only once. assert(SoFieldContainer::classTypeId == SoType::badType()); // Make sure parent class has been initialized. assert(inherited::getClassTypeId() != SoType::badType()); SoFieldContainer::classTypeId = SoType::createType(inherited::getClassTypeId(), "FieldContainer", NULL); sofieldcontainer_userdata_dict = new UserDataMap; coin_atexit((coin_atexit_f*) sofieldcontainer_userdata_cleanup, CC_ATEXIT_NORMAL); sofieldcontainer_copydictstorage = new SbStorage(sizeof(sofieldcontainer_copydict), sofieldcontainer_construct_copydict, sofieldcontainer_destruct_copydict); coin_atexit((coin_atexit_f*) sofieldcontainer_copydict_cleanup, CC_ATEXIT_NORMAL); } // Overridden from parent class. SoType SoFieldContainer::getClassTypeId(void) { return SoFieldContainer::classTypeId; } /*! This method sets all fields which we are parenting to their respective default values. */ void SoFieldContainer::setToDefaults(void) { const SoFieldData * fd = this->getFieldData(); if (!fd) return; // Allocate a fresh template to retrieve values from. const SoFieldContainer * from = (const SoFieldContainer *) this->getTypeId().createInstance(); from->ref(); fd->overlay(this, from, FALSE); from->unref(); SoFieldList l; int n = this->getFields(l); for (int i=0; i < n; i++) l[i]->setDefault(TRUE); } /*! This method checks to see if the fields of this container have their default values, and returns \c TRUE if that is the case. */ SbBool SoFieldContainer::hasDefaultValues(void) const { const SoFieldData * fd = this->getFieldData(); if (!fd) return TRUE; // Allocate a fresh template to compare with. const SoFieldContainer * fc = (const SoFieldContainer *) this->getTypeId().createInstance(); fc->ref(); SbBool hasdefaultvalues = fd->isSame(this, fc); fc->unref(); return hasdefaultvalues; } /*! This method returns \c TRUE if the values of the fields of this and \a container are equal. */ SbBool SoFieldContainer::fieldsAreEqual(const SoFieldContainer * container) const { const SoFieldData * fd0 = this->getFieldData(); if (fd0 == NULL) { if (container->getFieldData() == NULL) return TRUE; return FALSE; } return fd0->isSame(this, container); } /*! \COININTERNAL This method copies the field values from container into this. The fields are assumed to be of the same type. The \a copyconnections flag decides whether the field connections are to be copied aswell. */ void SoFieldContainer::copyFieldValues(const SoFieldContainer * container, SbBool copyconnections) { const SoFieldData * fd0 = this->getFieldData(); if (fd0 == NULL) { if (container->getFieldData() == NULL) return; #if COIN_DEBUG SoDebugError::postInfo("SoFieldContainer::copyFieldValues", "tried to copy from fieldcontainer of wrong type"); return; #endif // COIN_DEBUG } fd0->overlay(this, container, copyconnections); } /*! This method parses the values of one or more fields from the \a fielddata string. The fields must be in the same format as the Open Inventor file format. \c TRUE is returned upon success, and \c FALSE otherwise. \sa get() */ SbBool SoFieldContainer::set(const char * const fielddata) { return this->set(fielddata, NULL); } /*! This methods stores the field data in the given \a fielddata string. The format of the returned string follows the Open Inventor file format. \sa set() */ void SoFieldContainer::get(SbString & fielddata) { this->get(fielddata, NULL); } /*! This method adds the fields in this container to the \a l argument, and returns the number of fields added. The virtual nature of this function means that it can be overridden to e.g. hide private fields. */ int SoFieldContainer::getFields(SoFieldList & fields) const { const SoFieldData * fielddata = this->getFieldData(); if ( fielddata == NULL ) return 0; const int numfields = fielddata->getNumFields(); int fieldsadded = 0; for ( int i = 0; i < numfields; i++ ) { SoField * field = fielddata->getField(this, i); assert(field != NULL); switch ( field->getFieldType() ) { // these field types are ignored case SoField::EVENTIN_FIELD: case SoField::EVENTOUT_FIELD: break; default: fields.append(field); fieldsadded++; break; } } return fieldsadded; } /*! Returns VRML eventIn and eventOut fields in addition to the "ordinary" fields. \sa getFields() */ int SoFieldContainer::getAllFields(SoFieldList & fields) const { const SoFieldData * fielddata = this->getFieldData(); if ( fielddata == NULL ) return 0; int numfields = fielddata->getNumFields(); for ( int i=0; i < numfields; i++ ) fields.append(fielddata->getField(this, i)); return numfields; } /*! This method returns a pointer to the field with \a name, or \c NULL if no such field exists. */ SoField * SoFieldContainer::getField(const SbName & name) const { const SoFieldData * fields = this->getFieldData(); if (!fields) return NULL; const int numfields = fields->getNumFields(); for (int i = 0; i < numfields; i++) { if (name == fields->getFieldName(i)) return fields->getField(this, i); } return (SoField *)NULL; } /*! This method returns a pointer to the eventIn with name \a name, or \c NULL if no such eventIn exists. */ SoField * SoFieldContainer::getEventIn(const SbName & name) const { SoField * f = this->getField(name); return (f && (f->getFieldType() == SoField::EVENTIN_FIELD)) ? f : NULL; } /*! This method returns a pointer to the eventOut with name \a name, or \c NULL if no such eventOut exists. */ SoField * SoFieldContainer::getEventOut(const SbName & name) const { SoField * f = this->getField(name); return (f && (f->getFieldType() == SoField::EVENTOUT_FIELD)) ? f : NULL; } /*! Finds the name of the given \a field and returns the value in the \a name argument. \c TRUE is returned if the field is contained within this instance, and \c FALSE otherwise. */ SbBool SoFieldContainer::getFieldName(const SoField * const field, SbName & name) const { const SoFieldData * fields = this->getFieldData(); if (!fields) return FALSE; int idx = fields->getIndex(this, field); if (idx == -1) return FALSE; name = fields->getFieldName(idx); return TRUE; } /*! This method sets whether notification will be propagated on changing the values of the contained fields. The old value of the flag is returned. Notification is default \e on. Turning off automatic notification can be useful for optimizing performance. During series of updates you may want to avoid the propagation of notifications upon every field value change if you make a lot of them. This is how you should handle those cases: \code SbBool autonotify = node->enableNotify(FALSE); // ... // Make modifications to fields of "node" here. // ... node->enableNotify(autonotify); node->touch(); \endcode \sa isNotifyEnabled() */ SbBool SoFieldContainer::enableNotify(const SbBool enable) { int & flags = this->donotify; const SbBool old = (flags & FLAG_DONOTIFY) != 0; flags &= ~FLAG_DONOTIFY; if (enable) flags |= FLAG_DONOTIFY; return old; } /*! This method returns whether notification of changes to the field values in the container is propagated to its auditors. \sa enableNotify() */ SbBool SoFieldContainer::isNotifyEnabled(void) const { const int flags = this->donotify; return (flags & FLAG_DONOTIFY) != 0; } /*! This method parses the values of one or more fields from the \a fielddata string. The fields must be in the same format as the Open Inventor file format. \c TRUE is returned upon success, and \c FALSE otherwise. We use the reference dictionary provided by \a in. \sa get() */ SbBool SoFieldContainer::set(const char * fielddata, SoInput * in) { const SoFieldData * fields = this->getFieldData(); if (!fields) { #if COIN_DEBUG SoDebugError::postInfo("SoFieldContainer::set", "tried to set values of non-existant fields"); #endif // COIN_DEBUG // Return TRUE here might seem strange, but I think its correct to // do it like this -- we're just supposed to read field values until // we can't do that anymore. mortene. return TRUE; } SoInput * readbuf = in ? new SoInput(in) : new SoInput; readbuf->setBuffer((void *)fielddata, strlen(fielddata)); SbBool dummy; SbBool ok = fields->read(readbuf, this, FALSE, dummy); delete readbuf; return ok; } // Used from get()'s SoOutput if the initial buffer is too small. static void * realloc_buffer(void * buffer, size_t newsize) { return realloc(buffer, newsize); } /*! Put names and values of fields from this instance in the \a fielddata string. Fields will be separated in the returned string by end-of-line characters. We use the reference dictionary provided by \a out. \sa set() */ void SoFieldContainer::get(SbString & fielddata, SoOutput * out) { const SoFieldData * fields = this->getFieldData(); if (!fields) { fielddata = ""; return; } SoOutput * output = out ? new SoOutput(out) : new SoOutput(); const size_t STARTSIZE = 256; void * buffer = malloc(STARTSIZE); output->setBuffer(buffer, STARTSIZE, realloc_buffer); output->setStage(SoOutput::COUNT_REFS); fields->write(output, this); output->setStage(SoOutput::WRITE); fields->write(output, this); size_t size; output->getBuffer(buffer, size); // Initialize fielddata to the default value if no fields are found. fielddata = ""; // getBuffer() might return a size of zero. If it does, no need to // try to strip off the header. if (size > 0) { // Strip off header. char * start = strstr((char *)buffer, "\n\n"); if (start != NULL) { start += 2; fielddata = start; } } free(buffer); delete output; } // Overridden from parent class. void SoFieldContainer::notify(SoNotList * l) { #if COIN_DEBUG && 0 // debug char c; SoDebugError::postInfo("SoFieldContainer::notify", "fc %p, list %p, stack %p", this, l, &c); #endif // debug const int flags = this->donotify; if (flags & FLAG_DONOTIFY) { if (l->getLastRec()->getType() == SoNotRec::PARENT) { // we were notified from a child node. Create a new SoNotRec. SoNotRec rec(this); l->append(&rec); inherited::notify(l); } else { // we were notififed from a field. No need to add a new // SoNotRec since the base pointer will point to us. assert(l->getLastRec()->getType() == SoNotRec::CONTAINER); assert(l->getLastRec()->getBase() == (SoBase*) this); inherited::notify(l); } } #if COIN_DEBUG && 0 // debug SoDebugError::postInfo("SoFieldContainer::notify", "DONE"); #endif // debug } /*! \COININTERNAL I can't make head or tails of this method, it seems to return TRUE no matter what the arguments are. */ SbBool SoFieldContainer::validateNewFieldValue(SoField * field, void * newval) { COIN_STUB(); return TRUE; } // Documented in superclass. Overridden from SoBase to make sure field // connections into other field containers are also accounted for. void SoFieldContainer::addWriteReference(SoOutput * out, SbBool isfromfield) { // only count the fields if !isfromfield and this is the first time // that this node is counted (shouldWrite() is FALSE). SbBool countfields = !isfromfield && (SoWriterefCounter::instance(out)->shouldWrite(this) == FALSE); inherited::addWriteReference(out, isfromfield); // Avoid doing too many references to fields (and nodes, engines and // paths _within_ certain field types). if (countfields) { const SoFieldData * fd = this->getFieldData(); if (fd) fd->write(out, this); } } // Note that this documentation will also be used for all subclasses // which reimplements the method, so keep the doc "generic enough". /*! Writes all the fields contained in this instance to the output stream within \a out. This method is solely called from the write methods of fields. Either from SoField if the write is done because of a field-to-field connection, or from one of the fields which may actually write SoFieldContainer instances, i.e. SoSFNode, SoMFNode, SoSFEngine, SoMFEngine, SoSFPath and SoMFPath. This method, in addition to the ordinary write() method of SoNode, needs to be present since the fields don't have a write action instance in their writeValue() method, and writeInstance() will create a new SoWriteAction and call continueToApply() on it. */ void SoFieldContainer::writeInstance(SoOutput * out) { const SoFieldData * fd = this->getFieldData(); if (fd) fd->write(out, this); } /*! Returns \c TRUE if this object is instantiated from one of the native Coin classes, \c FALSE if the object's class is outside the standard Coin library. */ SbBool SoFieldContainer::getIsBuiltIn(void) const { return this->isBuiltIn; } /*! Returns a pointer to the class-wide field data storage object for this instance. If no fields are present, returns \c NULL. */ const SoFieldData * SoFieldContainer::getFieldData(void) const { return NULL; } // Note: the following documentation is used for all subclasses of // SoFieldContainer (all the way down to nodekits, draggers and // manipulators, for instance), so keep it general. /*! Makes a deep copy of all data of \a from into this instance, \e except external scenegraph references if \a copyconnections is \c FALSE. This is the protected method that should be overridden by extension node / engine / dragger / whatever subclasses which needs to account for internal data that are not handled automatically. For copying nodes from application code, you should not invoke this function directly, but rather call the SoNode::copy() function: \code SoNode * mynewnode = templatenode->copy(); \endcode The same also goes for engines. Make sure that when you override the copyContents() method in your extension class that you also make it call upwards to it's parent superclass in the inheritance hierarchy, as copyContents() in for instance SoNode and SoFieldContainer does important work. It should go something like this: \code void MyCoinExtensionNode::copyContents(const SoFieldContainer * from, SbBool copyconnections) { // let parent superclasses do their thing (copy fields, copy // instance name, etc etc) SoNode::copyContents(from, copyconnections); // [..then copy internal data..] } \endcode */ void SoFieldContainer::copyContents(const SoFieldContainer * from, SbBool copyconnections) { this->setName(from->getName()); this->donotify = from->donotify; this->copyFieldValues(from, copyconnections); } /*! Return copy of this instance. Note: default implementation just returns \c this pointer, SoNode and SoEngine overrides this method to return the pointer to the actual copy. */ SoFieldContainer * SoFieldContainer::copyThroughConnection(void) const { // Cast away const. return (SoFieldContainer *)this; } /*! \COININTERNAL Initialize a dictionary hash storing pointers for original fieldcontainer instances and their copies during scene graph copy operations. This method is called from the start of SoNode::copy(). */ void SoFieldContainer::initCopyDict(void) { sofieldcontainer_copydict * copydict = sofieldcontainer_get_copydict(); // Create two new dictionaries and _insert_ them in slot 0 in their // corresponding parallel lists, so they are stacked atop of the // already existing copy dictionaries (if any). // Push on stack. copydict->copiedinstancestack->insert(new SoFieldContainerCopyMap, 0); copydict->contentscopiedstack->insert(new ContentsCopiedMap, 0); } /*! Add a pair of pointers to an original fieldcontainer and a copy of it to an internal pointer dictionary used during scene graph copy operations. */ void SoFieldContainer::addCopy(const SoFieldContainer * orig, const SoFieldContainer * copy) { sofieldcontainer_copydict * copydict = sofieldcontainer_get_copydict(); // ref() so that we can guarantee that the copy is not destructed // while we're copying. This fixes a nodekit/dragger copy problem // (for instance when copying SoDragPointDragger). FIXME: figure // out exactly _why_ it is necessary to ref/unref the copied // instances. We need to track down exactly what happens in the // draggers that triggers an assert when copied. pederb, 2003-06-30 copy->ref(); SoFieldContainerCopyMap * copiedinstances = (*(copydict->copiedinstancestack))[0]; ContentsCopiedMap * contentscopied = (*(copydict->contentscopiedstack))[0]; assert(copiedinstances); assert(contentscopied); SbBool s = copiedinstances->put(orig, copy); assert(s); s = contentscopied->put(orig, FALSE); assert(s); } /*! Returns the copy of \a orig, if any. If no copy exists, we return a \c NULL pointer. */ SoFieldContainer * SoFieldContainer::checkCopy(const SoFieldContainer * orig) { sofieldcontainer_copydict * copydict = sofieldcontainer_get_copydict(); SoFieldContainerCopyMap * copiedinstances = (*(copydict->copiedinstancestack))[0]; assert(copiedinstances); const SoFieldContainer * fccopy; // FIXME: ugly constness cast. 20050520 mortene. return (SoFieldContainer *)(copiedinstances->get(orig, fccopy) ? fccopy : NULL); } /*! Returns a copy of \a orig. If no copy has been made earlier when the function is called, a copy is made on-the-fly (which is the reason we need to pass along the \a copyconnections flag). This method will also run the copyContents() method on the copy, if it hasn't been run already. Note that if you call copyContents() on the copy outside of this method, this will go undetected and the guts of \a orig will be copied multiple times into its copy. If copyContents() is called directly (instead of using copy()), it's assumed that the user only wants to copy the field values, and we just return NULL here. This is done to match how it's done in SGI Inventor. \sa checkCopy() */ SoFieldContainer * SoFieldContainer::findCopy(const SoFieldContainer * orig, const SbBool copyconnections) { sofieldcontainer_copydict * copydict = sofieldcontainer_get_copydict(); // Sometimes copyContents() is called directly (initCopyDict() is // not called first) to just copy the fields of a single node. This // is often done in SGI example code. We should just return NULL // here in those cases to be compatible with SGI Inventor. if (copydict->copiedinstancestack->getLength() == 0) return NULL; SoFieldContainerCopyMap * copiedinstances = (*(copydict->copiedinstancestack))[0]; ContentsCopiedMap * contentscopied = (*(copydict->contentscopiedstack))[0]; assert(copiedinstances); assert(contentscopied); SoProtoInstance * protoinst = SoProtoInstance::findProtoInstance((SoNode*) orig); SoFieldContainer * cp = SoFieldContainer::checkCopy(orig); if (!cp) { if (protoinst) { // We need to do some extra work when copying nodes that are // ProtoInstance root nodes. We create a new ProtoInstance node, // and register its root node as the copy. pederb, 2002-06-17 SoProto * proto = protoinst->getProtoDefinition(); SoProtoInstance * newinst = proto->createProtoInstance(); if (protoinst->getName().getLength()) { newinst->setName(protoinst->getName()); } cp = newinst->getRootNode(); // We have to call addCopy() before calling copyContents() since // the proto instance might have a field that has a pointer to // the root node. pederb, 2002-09-04 newinst->copyContents(protoinst, FALSE); } else { if (orig->isOfType(SoProto::getClassTypeId())) { // just copy the pointer. A PROTO definition is // read-only. It's not possible to change it after it has been // created so this should be safe. cp = (SoFieldContainer*) orig; } else { cp = (SoFieldContainer *)orig->getTypeId().createInstance(); } assert(cp); SoFieldContainer::addCopy(orig, cp); } } // Don't call copyContents for the proto instance root node, since // this is handled by the Proto node. if (!protoinst) { SbBool copied = FALSE; SbBool chk = contentscopied->get(orig, copied); assert(chk); if (!copied) { // we have to update the dictionary _before_ calling // copyContents in case we have an SoSFNode field in the node // that has a pointer to the node. Example scene graph: // // DEF mynode Script { // field SFNode self USE mynode // } // // pederb, 2002-09-04 chk = contentscopied->put(orig, TRUE); assert(!chk && "the key already exists"); cp->copyContents(orig, copyconnections); } } return cp; } // // Used to unref() copied instances (we ref() in addCopy()). // static void fieldcontainer_unref_node(const SoFieldContainer * const & key, const SoFieldContainer * const & fieldcontainer, void * closure) { // unref() (not unrefNoDelete()) so that unused nodes are // destructed (can happen during nodekit copy) fieldcontainer->unref(); } /*! \COININTERNAL Clean up the dictionary hash. This method is called from the end of SoNode::copy(). */ void SoFieldContainer::copyDone(void) { sofieldcontainer_copydict * copydict = sofieldcontainer_get_copydict(); SoFieldContainerCopyMap * copiedinstances = (*(copydict->copiedinstancestack))[0]; ContentsCopiedMap * contentscopied = (*(copydict->contentscopiedstack))[0]; assert(copiedinstances); assert(contentscopied); // unref all copied instances. See comment in addCopy(). copiedinstances->apply(fieldcontainer_unref_node, NULL); delete copiedinstances; delete contentscopied; // Pop off stack. copydict->copiedinstancestack->remove(0); copydict->contentscopiedstack->remove(0); } // Documented in superclass. SbBool SoFieldContainer::readInstance(SoInput * in, unsigned short flags) { const SoFieldData * fd = this->getFieldData(); if (fd) { SbBool notbuiltin; return fd->read(in, this, // The "error on unknown field" is FALSE when // we're a group node, since fields can be // followed by children node specifications. (flags & SoBase::IS_GROUP) ? FALSE : TRUE, notbuiltin); } return TRUE; } /*! Set a generic user data pointer for this field container. This function can be used by the application programmer to, for instance, store a pointer to an application specific data structure that is in some way related to the field container. getUserData() can later be used to retrieve the pointer. \sa getUserData() \since Coin 2.0 */ void SoFieldContainer::setUserData(void * userdata) const { (void)sofieldcontainer_userdata_dict->put(this, userdata); } /*! Return the generic user data pointer for this field container, or NULL if no user data has been set. \sa setUserData() \since Coin 2.0 */ void * SoFieldContainer::getUserData(void) const { void * tmp = NULL; if (sofieldcontainer_userdata_dict->get(this, tmp)) { return tmp; } return NULL; } #undef FLAG_DONOTIFY #undef FLAG_FIRSTINSTANCE