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