1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SoFieldData SoFieldData.h Inventor/fields/SoFieldData.h
35   \brief The SoFieldData class is a container for a prototype set of fields.
36 
37   \ingroup fields
38 
39   This class is instantiated once for each class of objects which use
40   fields, and which needs to be able to import and export them.
41 
42   Each field of a class is stored with the name it has been given
43   within its "owner" class and a pointer offset to the dynamic
44   instance of the field itself.
45 
46   Enumeration sets are stored with (name, value) pairs, to make it
47   possible to address, read and save enum type fields by name.
48 
49   It is unlikely that application programmers should need to use any
50   of the methods of this class directly.
51 
52   \sa SoField, SoFieldContainer
53 */
54 
55 // *************************************************************************
56 
57 // FIXME: Some methods related to reading VRML 2 files are
58 // missing. ????-??-?? pederb.
59 
60 /* IMPORTANT NOTE:
61  * If you make any changes (bugfixes, improvements) in this class,
62  * remember to also check the SoEngineOutputData class, as it is
63  * heavily based on this class.
64  */
65 
66 // *************************************************************************
67 
68 #include <Inventor/fields/SoFieldData.h>
69 
70 #include <cctype>
71 
72 #include <Inventor/SbName.h>
73 #include <Inventor/SoInput.h>
74 #include <Inventor/SoOutput.h>
75 #include <Inventor/errors/SoDebugError.h>
76 #include <Inventor/errors/SoReadError.h>
77 #include <Inventor/fields/SoField.h>
78 #include <Inventor/fields/SoFieldContainer.h>
79 #include <Inventor/lists/SoFieldList.h>
80 #include <Inventor/misc/SoProto.h>
81 
82 #include "threads/threadsutilp.h"
83 #include "io/SoInputP.h"
84 #include "coindefs.h" // COIN_STUB()
85 
86 // *************************************************************************
87 
88 static const char OPEN_BRACE_CHAR = '[';
89 static const char CLOSE_BRACE_CHAR = ']';
90 static const char VALUE_SEPARATOR_CHAR = ',';
91 
92 // *************************************************************************
93 
94 class SoFieldEntry {
95 public:
SoFieldEntry(const char * n,ptrdiff_t o)96   SoFieldEntry(const char * n, ptrdiff_t o) : name(n), ptroffset(o) { }
97   // Copy constructors.
SoFieldEntry(const SoFieldEntry * fe)98   SoFieldEntry(const SoFieldEntry * fe) { this->copy(fe); }
SoFieldEntry(const SoFieldEntry & fe)99   SoFieldEntry(const SoFieldEntry & fe) { this->copy(&fe); }
100 
operator ==(const SoFieldEntry & fe) const101   int operator==(const SoFieldEntry & fe) const {
102     // don't consider ptroffset here, since this will not be equal
103     // for fields containers with dynamic fields.
104     return (this->name == fe.name);
105   }
operator !=(const SoFieldEntry & fe) const106   int operator!=(const SoFieldEntry & fe) const {
107     return ! operator==(&fe);
108   }
109 
110   SbName name;
111   ptrdiff_t ptroffset;
112 
113 private:
copy(const SoFieldEntry * fe)114   void copy(const SoFieldEntry * fe) {
115     this->name = fe->name;
116     this->ptroffset = fe->ptroffset;
117   }
118 };
119 
120 class SoEnumEntry {
121 public:
SoEnumEntry(const SbName & name)122   SoEnumEntry(const SbName & name) : nameoftype(name) { }
123   // Copy constructors.
SoEnumEntry(const SoEnumEntry * ee)124   SoEnumEntry(const SoEnumEntry * ee) { this->copy(ee); }
SoEnumEntry(const SoEnumEntry & ee)125   SoEnumEntry(const SoEnumEntry & ee) { this->copy(&ee); }
126 
operator ==(const SoEnumEntry & ee) const127   int operator==(const SoEnumEntry & ee) const {
128     return ((this->nameoftype == ee.nameoftype) &&
129             (this->names == ee.names) && (this->values == ee.values));
130   }
operator !=(const SoEnumEntry & ee) const131   int operator!=(const SoEnumEntry & ee) const { return ! operator==(&ee); }
132 
133   SbName nameoftype;
134   SbList<SbName> names;
135   SbList<int> values;
136 
137 private:
copy(const SoEnumEntry * ee)138   void copy(const SoEnumEntry * ee) {
139     this->nameoftype = ee->nameoftype;
140     this->names = ee->names;
141     this->values = ee->values;
142   }
143 };
144 
145 // *************************************************************************
146 
147 /*!
148   Default constructor.
149  */
SoFieldData(void)150 SoFieldData::SoFieldData(void)
151 {
152 }
153 
154 /*!
155   Copy constructor.
156  */
SoFieldData(const SoFieldData & fd)157 SoFieldData::SoFieldData(const SoFieldData & fd)
158 {
159   this->copy(&fd);
160 }
161 
162 /*!
163   Copy constructor taking a pointer value as an argument. Handles \c
164   NULL pointers by behaving like the default constructor.
165 */
SoFieldData(const SoFieldData * fd)166 SoFieldData::SoFieldData(const SoFieldData * fd)
167 {
168   if (fd) this->copy(fd);
169 }
170 
171 /*!
172   Constructor. Takes an indication on the number of fields which
173   should be stored, to make sure the memory handling is efficient.
174  */
SoFieldData(int numfields)175 SoFieldData::SoFieldData(int numfields)
176   : fields(numfields)
177 {
178 }
179 
180 /*!
181   Destructor.
182  */
~SoFieldData()183 SoFieldData::~SoFieldData()
184 {
185   this->freeResources();
186 }
187 
188 // Empties internal lists, while deallocating the memory used for the
189 // entries.
190 void
freeResources(void)191 SoFieldData::freeResources(void)
192 {
193   for (int i=0; i < this->fields.getLength(); i++) delete this->fields[i];
194   this->fields.truncate(0);
195 
196   for (int j=0; j < this->enums.getLength(); j++) delete this->enums[j];
197   this->enums.truncate(0);
198 }
199 
200 /*!
201   Add a new field to our internal list.
202 
203   The \a name will be stored along with an pointer offset between \a
204   base and \a field, which will be valid for all instances of the
205   class type of \a base.
206 */
207 void
addField(SoFieldContainer * base,const char * name,const SoField * field)208 SoFieldData::addField(SoFieldContainer * base, const char * name,
209                       const SoField * field)
210 {
211   // FIXME: one peculiar thing I discovered while debugging something
212   // else. It appears the GlobalField::realTime is added twice -- how
213   // come? 20050708 mortene.
214   //
215   // FIXME: and another peculiar thing. Why is the SoInfo::string
216   // field added upon SoDB::init()? 20050708 mortene.
217 
218   CC_GLOBAL_LOCK;
219 
220   // Will be called many times, from each node constructor, for every
221   // field of the node. We're only interested in in getting this
222   // information /once/, however.
223   if (!this->hasField(name)) {
224 
225 #if COIN_DEBUG && 0 // debug
226     SoDebugError::postInfo("SoFieldData::addField",
227                            "class==%s, fieldname==%s, field==%p, index==%d",
228                            base->getTypeId().getName().getString(),
229                            name, field, this->fields.getLength());
230 #endif // debug
231 
232     size_t vbase = reinterpret_cast<size_t>(base);
233     size_t vfield = reinterpret_cast<size_t>(field);
234     const ptrdiff_t offs = vfield - vbase;
235 
236     // FIXME: disabled yet, as we should first make a test program to
237     // see if this robustness check is ok with the current Coin
238     // code. The check shuold simply run through all
239     // SoFieldContainer-derived classes and make an instance of all
240     // non-abstract ones. Then see if there'll be any asserting or
241     // crashing from the below check.
242     //
243     // This would smoke out any internal faulty use of
244     // SoFieldData::addField().
245     //
246     // 20050708 mortene.
247 #if 0
248     // Robustness: check whether or not the given field is actually a
249     // member of base.
250     const SoField * f = (const SoField *)(base + offs);
251     // The next is likely to segfault from the isOfType() call if
252     // there's an error (and not actually assert).
253     assert(f->isOfType(SoField::getClassTypeId()));
254 #endif
255 
256     this->fields.append(new SoFieldEntry(name, offs));
257   }
258   CC_GLOBAL_UNLOCK;
259 }
260 
261 /*!
262   Copy fields from container \a from to container \a to. If
263   \a copyconnections is \c TRUE, we'll also copy the connections
264   field \a from has made.
265 
266   If you think the method signature is a bit strange, you're correct.
267   This should really have been a static method (the owner \c this
268   instance of the method isn't used at all, due to how the internal
269   representation of field template list are stored), but for unknown
270   reasons this is a dynamic method in Open Inventor. So also in Coin,
271   to keep compatibility.
272  */
273 void
overlay(SoFieldContainer * to,const SoFieldContainer * from,SbBool copyconnections) const274 SoFieldData::overlay(SoFieldContainer * to, const SoFieldContainer * from,
275                      SbBool copyconnections) const
276 {
277   if (to == from) return;
278 
279   const SoFieldData * fd0 = to->getFieldData();
280   const SoFieldData * fd1 = from->getFieldData();
281   if (!fd0 && !fd1) return;
282 
283   // The field containers should have equal SoFieldData sets.
284   assert(fd0 && fd1 && *fd0==*fd1);
285 
286   const int num = fd0->getNumFields();
287   for (int i = 0; i < num; i++) {
288     SoField * field0 = fd0->getField(to, i);
289     SoField * field1 = fd1->getField(from, i);
290     // copy value only if necessary (note how SoTexture2::filename and
291     // SoTexture2::image would affect each other without this test)
292     if ( !field0->isDefault() || !field1->isDefault() ) {
293       field0->copyFrom(*field1);
294       field0->setDefault(field1->isDefault());
295     }
296     // copy flags
297     field0->setIgnored(field1->isIgnored());
298     field0->enableNotify(field1->isNotifyEnabled());
299     field0->setFieldType(field1->getFieldType());
300 
301     // fix complex fields (node, engine, and path fields)
302     field0->fixCopy(copyconnections);
303     // handle connections
304     if (copyconnections) field0->copyConnection(field1);
305   }
306 }
307 
308 /*!
309   Returns number of fields contained within this instance.
310  */
311 int
getNumFields(void) const312 SoFieldData::getNumFields(void) const
313 {
314   return this->fields.getLength();
315 }
316 
317 /*!
318   Returns the name of the field at \a index.
319  */
320 const SbName &
getFieldName(int index) const321 SoFieldData::getFieldName(int index) const
322 {
323   return this->fields[index]->name;
324 }
325 
326 /*!
327   Returns a pointer to the field at \a index within the \a object
328   instance.
329  */
330 SoField *
getField(const SoFieldContainer * object,int index) const331 SoFieldData::getField(const SoFieldContainer * object, int index) const
332 {
333   assert(index >= 0 && index < this->fields.getLength());
334   size_t fieldptr = reinterpret_cast<size_t>(object);
335   fieldptr += this->fields[index]->ptroffset;
336   return reinterpret_cast<SoField *>(fieldptr);
337 }
338 
339 /*!
340   Returns the internal index value of \a field in \a fc. If \a field
341   is not part of \a fc, returns -1.
342 */
343 int
getIndex(const SoFieldContainer * fc,const SoField * field) const344 SoFieldData::getIndex(const SoFieldContainer * fc, const SoField * field) const
345 {
346   size_t vbase = reinterpret_cast<size_t>(fc);
347   size_t vfield = reinterpret_cast<size_t>(field);
348   const ptrdiff_t ptroffset = vfield - vbase;
349 
350   for (int i=0; i < this->fields.getLength(); i++)
351     if (this->fields[i]->ptroffset == ptroffset) return i;
352 
353   return -1;
354 }
355 
356 /*!
357   Either adds a new enum set (with an initial member), or adds a new value
358   member to an existing enum set.
359 */
360 void
addEnumValue(const char * enumname,const char * valuename,int value)361 SoFieldData::addEnumValue(const char * enumname, const char * valuename,
362                           int value)
363 {
364   CC_GLOBAL_LOCK;
365   if (!this->hasEnumValue(enumname, valuename)) {
366     SoEnumEntry * e = NULL;
367 
368     for (int i=0; !e && (i < this->enums.getLength()); i++) {
369       if (this->enums[i]->nameoftype == enumname) e = this->enums[i];
370     }
371 
372     if (e == NULL) {
373       e = new SoEnumEntry(enumname);
374       this->enums.append(e);
375     }
376 
377 #if COIN_DEBUG && 0 // debug
378     SoDebugError::postInfo("SoFieldData::addEnumValue",
379                            "enumname: %s, valuename: %s, value: %d",
380                            enumname, valuename, value);
381 #endif // debug
382 
383     assert(e->names.find(valuename) == -1);
384     e->names.append(valuename);
385     // Note that an enum can have several names mapping to the same
386     // value. 20000101 mortene.
387     e->values.append(value);
388   }
389   CC_GLOBAL_UNLOCK;
390 }
391 
392 /*!
393   Returns the \a names and \a values of enumeration entry with the \a
394   enumname. The number of (name, value) pairs available in the
395   enumeration is returned in \a num.
396 */
397 void
getEnumData(const char * enumname,int & num,const int * & values,const SbName * & names)398 SoFieldData::getEnumData(const char * enumname, int & num,
399                          const int *& values, const SbName *& names)
400 {
401   num = 0;
402   values = NULL;
403   names = NULL;
404 
405   for (int i=0; i < this->enums.getLength(); i++) {
406     SoEnumEntry * e = this->enums[i];
407     if (e->nameoftype == enumname) {
408       num = e->names.getLength();
409       if (num) {
410         assert(e->names.getLength() == e->values.getLength());
411         names = e->names.getArrayPtr();
412         values = e->values.getArrayPtr();
413       }
414       return;
415     }
416   }
417 }
418 
419 /*!
420   Read field data from the \a in stream for fields belonging to \a
421   object. Returns \c TRUE if everything went ok, or \c FALSE if any
422   error conditions occurs.
423 
424   \a erroronunknownfield decides whether or not \c FALSE should be
425   returned if a name identifier not recognized as a fieldname of \a
426   object is encountered. Note that \a erroronunknownfield should be \c
427   FALSE if \a object is a container with child objects, otherwise the
428   code will fail upon the first child name specification.
429 
430   If \a notbuiltin is \c TRUE on return, \a object is an unknown node
431   or engine type. Unknown nodes are recognized by the \c fields
432   keyword first in their file format definition, and unknown engines
433   by the \c inputs keyword.
434 
435 */
436 SbBool
read(SoInput * in,SoFieldContainer * object,SbBool erroronunknownfield,SbBool & notbuiltin) const437 SoFieldData::read(SoInput * in, SoFieldContainer * object,
438                   SbBool erroronunknownfield, SbBool & notbuiltin) const
439 {
440   notbuiltin = FALSE;
441 
442   if (in->isBinary()) {
443     unsigned int fieldsval;
444     if (!in->read(fieldsval)) {
445       SoReadError::post(in, "Premature EOF");
446       return FALSE;
447     }
448 
449     uint8_t numfields = static_cast<uint8_t>(fieldsval & 0xff);
450     uint8_t fieldflags = static_cast<uint8_t>(fieldsval >> 8);
451 
452     if (SoInputP::debugBinary()) {
453       SoDebugError::postInfo("SoFieldData::read",
454                              "fieldsval==0x%08x => "
455                              "flags==0x%02x numfields==%u (0x%02x)",
456                              fieldsval, fieldflags, numfields);
457     }
458 
459     // Unknown node type, must read field descriptions.
460     if (fieldflags & SoFieldData::NOTBUILTIN) {
461       if (!this->readFieldDescriptions(in, object, numfields)) return FALSE;
462     }
463 
464     // Check for more flags, in case there's any we've missed.
465     if (fieldflags & ~(SoFieldData::NOTBUILTIN)) {
466       SoReadError::post(in,
467                         "Unknown flags in control word: 0x%02x, "
468                         "please report to coin-support@sim.no",
469                         fieldflags);
470     }
471 
472     if (numfields > this->fields.getLength()) {
473       SoDebugError::postWarning("SoFieldData::read",
474                                 "The number of fields to read for a %s "
475                                 "node in this binary file is given as %d. "
476                                 "This is suspicious as this node type "
477                                 "doesn't have more than %d distinct fields. "
478                                 "The file is likely to be corrupt.",
479                                 object->getTypeId().getName().getString(),
480                                 numfields, this->fields.getLength());
481     }
482 
483     if (numfields == 0) return TRUE;
484 
485     for (int i=0; i < numfields; i++) {
486       SbName fieldname;
487       if (!in->read(fieldname, TRUE) || !fieldname) {
488         SoReadError::post(in, "Couldn't read the name of field number %d", i);
489         return FALSE;
490       }
491 
492       if (SoInputP::debugBinary()) {
493         SoDebugError::postInfo("SoFieldData::read",
494                                "fieldname=='%s'", fieldname.getString());
495       }
496 
497       SbBool foundname;
498       if (!this->read(in, object, fieldname, foundname)) {
499         if (!foundname) SoReadError::post(in, "Unknown field \"%s\" in \"%s\"",
500                                           fieldname.getString(),
501                                           object->getTypeId().getName().getString());
502         return FALSE;
503       }
504     }
505   }
506   else { // ASCII format.
507     SbBool firstidentifier = TRUE;
508     SbName ROUTE_KEYWORD("ROUTE");
509     SbName PROTO_KEYWORD("PROTO");
510     SbName EXTERNPROTO_KEYWORD("EXTERNPROTO");
511     while (TRUE) {
512       SbName fieldname;
513       if (!in->read(fieldname, TRUE)) return TRUE; // Terminates loop on "}"
514 
515       if (in->isFileVRML2()) {
516         // test for the VRML97 ROUTE keyword
517         if (fieldname == ROUTE_KEYWORD) {
518           if (!SoBase::readRoute(in)) return FALSE;
519           continue; // skip to next field/route
520         }
521         // test for the VRML97 PROTO/EXTERNPROTO keyword
522         if (fieldname == PROTO_KEYWORD || fieldname == EXTERNPROTO_KEYWORD) {
523           SoProto * proto = new SoProto(fieldname == EXTERNPROTO_KEYWORD);
524           proto->ref();
525           if (proto->readInstance(in, 0)) {
526             proto->unrefNoDelete();
527             in->addProto(proto);
528           }
529           else {
530             proto->unref();
531             SoReadError::post(in, "Error while parsing PROTO definition inside node");
532             return FALSE;
533           }
534           continue;  // skip to next field/route
535         }
536       }
537 
538       SbBool readok;
539       if (in->checkISReference(object, fieldname, readok)) {
540         continue; // skip to next field
541       }
542       if (!readok) {
543         SoReadError::post(in, "Error while searching for IS keyword for field \"%s\"",
544                           fieldname.getString());
545         return FALSE;
546       }
547       // This should be caught in SoInput::read(SbName, SbBool).
548       assert(fieldname != "");
549 
550       SbBool foundname;
551       if (!this->read(in, object, fieldname, foundname) && foundname)
552         return FALSE;
553 
554       if (!foundname) {
555         // User extension node with explicit field definitions.
556         if (firstidentifier && fieldname == "fields") {
557           notbuiltin = TRUE;
558           if (!this->readFieldDescriptions(in, object, 0)) return FALSE;
559         }
560         // User extension engine with explicit input field definitions.
561         else if (firstidentifier && fieldname == "inputs") {
562           notbuiltin = TRUE;
563           // FIXME: read input defs and inputs (and output
564           // defs?). 20000102 mortene.
565           COIN_STUB();
566           return FALSE;
567         }
568         else if (erroronunknownfield) {
569           SoReadError::post(in, "Unknown field \"%s\" in \"%s\"",
570                             fieldname.getString(),
571                             object->getTypeId().getName().getString());
572           return FALSE;
573         }
574         else {
575           in->putBack(fieldname.getString());
576           return TRUE;
577         }
578       }
579       firstidentifier = FALSE;
580     }
581   }
582 
583   return TRUE;
584 }
585 
586 /*!
587   Find field \a fieldname in \a object, and if it is available, set
588   \a foundname to \c TRUE and try to read the field specification
589   from \a in. If \a foundname is set to \c TRUE, the return value
590   says whether or not the field specification could be read without
591   any problems.
592 
593   If \a fieldname is not part of \a object, returns \c FALSE with \a
594   foundname also set to \c FALSE.
595 */
596 SbBool
read(SoInput * in,SoFieldContainer * object,const SbName & fieldname,SbBool & foundname) const597 SoFieldData::read(SoInput * in, SoFieldContainer * object,
598                   const SbName & fieldname, SbBool & foundname) const
599 {
600   for (int i = 0; i < this->fields.getLength(); i++) {
601     if (fieldname == this->getFieldName(i)) {
602       foundname = TRUE;
603       return this->getField(object, i)->read(in, fieldname);
604     }
605   }
606 
607   foundname = FALSE;
608 
609   // Should return TRUE, according to how this function is supposed to
610   // work: it should only return FALSE on actual /parse/ errors, and
611   // not "just" when the name of the read field is unknown.
612   //
613   // An example where this is necessary is where field names don't
614   // match up directly for nodekits, but the field is actually in a
615   // nested nodekit (i.e. a nodekit within another nodekit's catalog),
616   // or is a composite name for a field in a nested nodekit.
617   return TRUE;
618 }
619 
620 /*!
621   Write to \a out field names and field values for the fields of
622   \a object.
623  */
624 void
write(SoOutput * out,const SoFieldContainer * object) const625 SoFieldData::write(SoOutput * out, const SoFieldContainer * object) const
626 {
627   // In Coin, we always write field description for all fields in
628   // extension nodes. This means that we also need to write all fields
629   // for the binary format, since the number of fields and field
630   // descriptions is printed in a byte before the field
631   // descriptions. Phew, the OIV binary format sucks....
632   SbBool writeallfields = out->isBinary() && ! object->getIsBuiltIn();
633 
634   uint16_t i;
635 
636   if (out->getStage() == SoOutput::COUNT_REFS) {
637     // Handle first stage of write operations.
638     for (i=0; i < this->getNumFields(); i++) {
639       SoField * f = this->getField(object, i);
640       if (writeallfields || f->shouldWrite()) {
641         f->write(out, this->getFieldName(i));
642       }
643     }
644     return;
645   }
646   // Ok, we've passed the first write stage and is _really_ writing.
647   assert((out->getStage() == SoOutput::WRITE) && "unknown write stage");
648 
649   // FIXME: is this really the best place to write the flags +
650   // numfields value? 20000102 mortene.
651   if (out->isBinary()) {
652     // Check how many fields will be written.
653     uint8_t numfields = 0;
654     for (int j = 0; j < this->getNumFields(); j++) {
655       const SoField * f = this->getField(object, j);
656       if (writeallfields || f->shouldWrite()) {
657         // This is an amazingly lame limitation, but we can't really
658         // fix it without breaking compatibility with the SGI binary
659         // .iv format.  (The moral of the story is: avoid binary
660         // .iv-files.)
661         assert((numfields < 255) &&
662                "too many fields to handle with binary .iv format");
663         numfields++;
664       }
665     }
666 
667     uint16_t fieldflags = 0x0000;
668     // FIXME: take care of setting flags for SoUnknownEngines, if
669     // necessary. 20000102 mortene.
670     if (!object->getIsBuiltIn()) fieldflags |= SoFieldData::NOTBUILTIN;
671 
672     // use unsigned int to match an SoOutput::write method
673     unsigned int w = static_cast<unsigned int>(fieldflags);
674     w <<= 8;
675     w |= numfields;
676 
677     out->write(w);
678   }
679 
680   // FIXME: write descriptions for SoUnknownEngine, if
681   // necessary. 20000102 mortene.
682   if (!object->getIsBuiltIn()) this->writeFieldDescriptions(out, object);
683 
684   SoProto * proto = out->getCurrentProto();
685 
686   for (i = 0; i < this->getNumFields(); i++) {
687     SoField * f = this->getField(object, i);
688     // Test if field has a PROTO IS reference
689     SbName pname = proto ?
690       proto->findISReference(object, this->getFieldName(i)) : SbName::empty();
691     if (pname.getLength()) {
692       out->indent();
693       out->write(this->getFieldName(i).getString());
694       out->write(" IS ");
695       out->write(pname.getString());
696       out->write("\n");
697     }
698     else if (writeallfields || f->shouldWrite()) {
699       f->write(out, this->getFieldName(i));
700     }
701   }
702 }
703 
704 
705 /*!
706   Copy contents of \a src into this instance.
707 
708   If there was any data set up in this instance before the method was
709   called, the old data is removed first.
710 
711   Note that this only copies the field set template specification from
712   \a src, \e not actual field contents. For copying field contents,
713   see the SoFieldData::overlay() method.
714 */
715 void
copy(const SoFieldData * src)716 SoFieldData::copy(const SoFieldData * src)
717 {
718   this->freeResources();
719 
720   int i;
721   for (i=0; i < src->fields.getLength(); i++) {
722     this->fields.append(new SoFieldEntry(src->fields[i]));
723   }
724   for (i=0; i < src->enums.getLength(); i++) {
725     this->enums.append(new SoEnumEntry(src->enums[i]));
726   }
727 }
728 
729 /*!
730   Compares \a c1 and \a c2 to see if they have the same field data set
731   and if the fields of \a c1 have the same values as the fields of \a c2.
732 
733   Field connections are not considered (i.e. we will return \c TRUE if
734   the values of the fields of \a c1 are equal to the fields of \a c2,
735   even if they differ in how they have made connections to other
736   fields).
737 
738   If you think the method signature is a bit strange, you're correct.
739   This should really have been a static method (the owner \c this
740   instance of the method isn't used at all, due to how the internal
741   representations of field template lists are stored), but for unknown
742   reasons this is a dynamic method in Open Inventor. So also in Coin,
743   to keep compatibility.
744 */
745 SbBool
isSame(const SoFieldContainer * c1,const SoFieldContainer * c2) const746 SoFieldData::isSame(const SoFieldContainer * c1,
747                     const SoFieldContainer * c2) const
748 {
749   if (c1 == c2) return TRUE;
750 
751   const SoFieldData * fd1 = c1->getFieldData();
752   const SoFieldData * fd2 = c2->getFieldData();
753   if (!fd1 && !fd2) return TRUE;
754   if (!fd1 || !fd2) return FALSE;
755   if (*fd1 != *fd2) return FALSE;
756 
757   int num = fd1->getNumFields();
758   for (int i=0; i < num; i++)
759     if (*(fd1->getField(c1, i)) != *(fd2->getField(c2, i))) return FALSE;
760 
761   return TRUE;
762 }
763 
764 /*!
765   Reads a set of field specifications from \a in for an unknown nodeclass type,
766   in the form "[ FIELDCLASS FIELDNAME, FIELDCLASS FIELDNAME, ... ]".
767 
768   \a numdescriptionsexpected is used for binary format import to know
769   how many descriptions should be parsed.
770 
771   If \a readfieldvalues is \e TRUE (the default), the field initial value
772   is expected after the field name in the SoInput stream.
773 
774 */
775 SbBool
readFieldDescriptions(SoInput * in,SoFieldContainer * object,int numdescriptionsexpected,const SbBool readfieldvalues) const776 SoFieldData::readFieldDescriptions(SoInput * in, SoFieldContainer * object,
777                                    int numdescriptionsexpected,
778                                    const SbBool readfieldvalues) const
779 {
780   // These two macros are convenient for reading with error detection.
781 #define READ_CHAR(c) \
782     if (!in->read(c)) { \
783       SoReadError::post(in, "Premature end of file"); \
784       return FALSE; \
785     }
786 
787   const SbName EVENTIN("eventIn");
788   const SbName EVENTOUT("eventOut");
789   const SbName FIELD("field");
790   const SbName EXPOSEDFIELD("exposedField");
791   const SbName IS("IS");
792 
793   char c;
794   if (!in->isBinary()) {
795     READ_CHAR(c);
796     if (c != OPEN_BRACE_CHAR) {
797       SoReadError::post(in, "Expected '%c', got '%c'", OPEN_BRACE_CHAR, c);
798       return FALSE;
799     }
800   }
801 
802   for (int j=0; !in->isBinary() || (j < numdescriptionsexpected); j++) {
803 
804     if (!in->isBinary()) {
805       READ_CHAR(c);
806       if (c == CLOSE_BRACE_CHAR) return TRUE;
807       else in->putBack(c);
808     }
809 
810     SbName fieldtypename;
811 
812     if (!in->read(fieldtypename, TRUE)) {
813       SoReadError::post(in, "Couldn't read name of field type");
814       return FALSE;
815     }
816 
817     SbName fieldtype("");
818     if (fieldtypename == EVENTIN ||
819         fieldtypename == EVENTOUT ||
820         fieldtypename == FIELD ||
821         fieldtypename == EXPOSEDFIELD) {
822       fieldtype = fieldtypename;
823       if (!in->read(fieldtypename, TRUE)) {
824         SoReadError::post(in, "Couldn't read name of field type");
825         return FALSE;
826       }
827     }
828 
829     SoType type = SoType::fromName(fieldtypename.getString());
830     if ((type == SoType::badType()) ||
831         !type.isDerivedFrom(SoField::getClassTypeId())) {
832       SoReadError::post(in, "Unknown field type '%s'",
833                         fieldtypename.getString());
834       return FALSE;
835     }
836     else if (!type.canCreateInstance()) {
837       SoReadError::post(in, "Abstract class type '%s'", fieldtypename.getString());
838       return FALSE;
839     }
840 
841     SbName fieldname;
842     if (!in->read(fieldname, TRUE)) {
843       SoReadError::post(in, "Couldn't read name of field");
844       return FALSE;
845     }
846 
847 
848 #if COIN_DEBUG && 0 // debug
849     SoDebugError::postInfo("SoFieldData::readFieldDescriptions",
850                            "type: ``%s'', name: ``%s''",
851                            fieldtypename.getString(), fieldname.getString());
852 #endif // debug
853 
854     SoField * newfield = NULL;
855     for (int i=0; !newfield && (i < this->fields.getLength()); i++) {
856       if (this->fields[i]->name == fieldname) {
857         newfield = this->getField(object, i);
858       }
859     }
860     if (!newfield) {
861       // Cast away const -- ugly.
862       SoFieldData * that = const_cast<SoFieldData *>(this);
863       newfield = static_cast<SoField *>(type.createInstance());
864       newfield->setContainer(object);
865       newfield->setDefault(TRUE);
866       that->addField(object, fieldname.getString(), newfield);
867     }
868 
869     if (fieldtype == EVENTIN || fieldtype == EVENTOUT) {
870       if (fieldtype == EVENTIN) {
871         newfield->setFieldType(SoField::EVENTIN_FIELD);
872       }
873       else {
874         newfield->setFieldType(SoField::EVENTOUT_FIELD);
875       }
876       SbBool readok;
877       (void) in->checkISReference(object, fieldname.getString(), readok);
878       if (!readok) {
879         SoReadError::post(in, "Error while searching for IS keyword for field '%s'",
880                           fieldname.getString());
881         return FALSE;
882       }
883     }
884     else if (fieldtype == FIELD || fieldtype == EXPOSEDFIELD) {
885       if (fieldtype == EXPOSEDFIELD) {
886         newfield->setFieldType(SoField::EXPOSED_FIELD);
887       }
888       if (readfieldvalues && !newfield->read(in, fieldname)) {
889         SoFieldContainer * fc = newfield->getContainer();
890         SbString s("");
891         if (fc) { s.sprintf(" of %s", fc->getTypeId().getName().getString()); }
892         SoReadError::post(in, "Unable to read value for field '%s'%s",
893                           fieldname.getString(), s.getString());
894         return FALSE;
895       }
896     }
897 
898     SbBool readok;
899     (void) in->checkISReference(object, fieldname, readok);
900     if (!readok) {
901       SoReadError::post(in, "Unable to search for IS keyword");
902       return FALSE;
903     }
904     if (!in->isBinary()) {
905       READ_CHAR(c);
906       if (c != VALUE_SEPARATOR_CHAR) in->putBack(c);
907       // (Allow missing value separators (i.e. no "," character
908       // between two field descriptions)).
909     }
910   }
911 
912 #undef READ_CHAR
913 
914   return TRUE;
915 }
916 
917 
918 /*!
919   Write a set of field specifications to \a out for an unknown nodeclass type,
920   in the form "[ FIELDCLASS FIELDNAME, FIELDCLASS FIELDNAME, ... ]".
921  */
922 void
writeFieldDescriptions(SoOutput * out,const SoFieldContainer * object) const923 SoFieldData::writeFieldDescriptions(SoOutput * out,
924                                     const SoFieldContainer * object) const
925 {
926   SoFieldList forwardlist;
927 
928   if (!out->isBinary()) {
929     out->indent();
930     out->write("fields [ ");
931   }
932 
933   SbBool atleastonewritten = FALSE;
934   for (int i = 0; i < this->getNumFields(); i++) {
935     const SoField * f = this->getField(object, i);
936     if (!out->isBinary() && atleastonewritten) out->write(", ");
937     out->write(static_cast<const char *>(f->getTypeId().getName()));
938     if (!out->isBinary()) out->write(' ');
939     out->write(static_cast<const char *>(this->getFieldName(i)));
940     atleastonewritten = TRUE;
941   }
942 
943   if (!out->isBinary()) out->write(" ]\n");
944 }
945 
946 // Check for equality.
947 int
operator ==(const SoFieldData * fd) const948 SoFieldData::operator==(const SoFieldData * fd) const
949 {
950   int i, n;
951   n = this->enums.getLength();
952   if (n != fd->enums.getLength()) return FALSE;
953   for (i = 0; i < n; i++) {
954     if (*(this->enums[i]) != *(fd->enums[i])) return FALSE;
955   }
956 
957   n = this->fields.getLength();
958   if (n != fd->fields.getLength()) return FALSE;
959   for (i = 0; i < n; i++) {
960     if (*(this->fields[i]) != *(fd->fields[i])) return FALSE;
961   }
962 
963   return TRUE;
964 }
965 
966 /*!
967   \internal
968   \since Coin 2.3
969 */
970 SbBool
hasField(const char * name) const971 SoFieldData::hasField(const char * name) const
972 {
973   for (int i = 0; i < this->fields.getLength(); i++) {
974     if (this->fields[i]->name == name) return TRUE;
975   }
976   return FALSE;
977 }
978 
979 /*!
980   \internal
981   \since Coin 2.3
982 */
983 SbBool
hasEnumValue(const char * enumname,const char * valuename)984 SoFieldData::hasEnumValue(const char * enumname, const char * valuename)
985 {
986   SoEnumEntry * e = NULL;
987 
988   for (int i=0; !e && (i < this->enums.getLength()); i++) {
989     if (this->enums[i]->nameoftype == enumname) e = this->enums[i];
990   }
991   if (e == NULL) return FALSE;
992   return e->names.find(valuename) != -1;
993 }
994