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 SoSFEnum SoSFEnum.h Inventor/fields/SoSFEnum.h
35   \brief The SoSFEnum class is a container for an enum value.
36 
37   \ingroup fields
38 
39   This field is used where nodes, engines or other field containers
40   needs to store one particular value out of an enumerated set.
41 
42   A field of this type stores its value to file as the symbolic
43   name, rather than the actual integer value.
44 
45   SoSFEnum instances are initialized on an instance basis, usually in
46   the constructor of the fieldcontainer with the macros
47   SO_NODE_DEFINE_ENUM_VALUE(enumtype, symbolvalue) and
48   SO_NODE_SET_SF_ENUM_TYPE(enumfield, enumtype) for nodes, or for
49   engines; SO_ENGINE_DEFINE_ENUM_VALUE() and
50   SO_ENGINE_SET_SF_ENUM_TYPE().
51 
52   Example initialization from the constructor of the SoCone class:
53   \code
54   SO_NODE_DEFINE_ENUM_VALUE(Part, SIDES);
55   SO_NODE_DEFINE_ENUM_VALUE(Part, BOTTOM);
56   SO_NODE_DEFINE_ENUM_VALUE(Part, ALL);
57   SO_NODE_SET_SF_ENUM_TYPE(parts, Part); \endcode
58 
59   \sa SoMFEnum, SoSFBitMask
60 */
61 
62 // *************************************************************************
63 
64 #include <Inventor/fields/SoSFEnum.h>
65 
66 #include <Inventor/fields/SoFieldContainer.h>
67 #include <Inventor/errors/SoReadError.h>
68 #include <Inventor/SoInput.h>
69 #include <Inventor/SoOutput.h>
70 #include <Inventor/errors/SoDebugError.h>
71 
72 #include "fields/SoSubFieldP.h"
73 
74 // *************************************************************************
75 
76 /*!
77   \var int SoSFEnum::numEnums
78   Number of enumeration mappings.
79 */
80 /*!
81   \var SbName * SoSFEnum::enumNames
82   Array of enumeration names. Maps 1-to-1 with the enumValues.
83 */
84 /*!
85   \var int * SoSFEnum::enumValues
86   Array of enumeration values. Maps 1-to-1 with the enumNames.
87 */
88 /*!
89   \var SbBool SoSFEnum::legalValuesSet
90   Is \c TRUE if a set of enum name-to-value mappings has been set.
91 */
92 
93 // *************************************************************************
94 
95 PRIVATE_TYPEID_SOURCE(SoSFEnum);
96 PRIVATE_EQUALITY_SOURCE(SoSFEnum);
97 
98 // *************************************************************************
99 
100 // (Declarations hidden in SO_[S|M]FIELD_HEADER macro in header file,
101 // so don't use Doxygen commenting.)
102 #ifndef DOXYGEN_SKIP_THIS
103 
104 /* Constructor. */
SoSFEnum(void)105 SoSFEnum::SoSFEnum(void)
106 {
107   this->enumValues = NULL;
108   this->enumNames  = NULL;
109   this->numEnums = 0;
110   this->legalValuesSet = FALSE;
111 }
112 
113 /* Destructor. */
~SoSFEnum()114 SoSFEnum::~SoSFEnum()
115 {
116   delete[] this->enumValues;
117   delete[] this->enumNames;
118 }
119 
120 // *************************************************************************
121 
122 /* Copy enumeration mappings and \a field value. */
123 const SoSFEnum &
operator =(const SoSFEnum & field)124 SoSFEnum::operator=(const SoSFEnum & field)
125 {
126   this->setEnums(field.numEnums, field.enumValues, field.enumNames);
127   this->setValue(field.getValue());
128   return *this;
129 }
130 
131 #endif // DOXYGEN_SKIP_THIS
132 
133 
134 // Override from parent class.
135 void
initClass(void)136 SoSFEnum::initClass(void)
137 {
138   SO_SFIELD_INTERNAL_INIT_CLASS(SoSFEnum);
139 }
140 
141 /*!
142   Makes a set of \a num enumeration \a names map to integer values,
143   given by \a vals.
144 */
145 void
setEnums(const int num,const int * vals,const SbName * names)146 SoSFEnum::setEnums(const int num, const int * vals, const SbName * names)
147 {
148   delete[] this->enumValues;
149   delete[] this->enumNames;
150 
151   this->enumValues = new int[num];
152   this->enumNames = new SbName[num];
153   this->numEnums = num;
154   this->legalValuesSet = TRUE;
155 
156   for (int i = 0; i < this->numEnums; i++) {
157     this->enumValues[i] = vals[i];
158     this->enumNames[i] = names[i];
159   }
160 }
161 
162 /*!
163   Return in \a val the enumeration value which matches the given
164   enumeration \a name.
165 
166   Returns \c TRUE if \a name is a valid enumeration string, otherwise
167   \c FALSE.
168 */
169 SbBool
findEnumValue(const SbName & name,int & val)170 SoSFEnum::findEnumValue(const SbName & name, int & val)
171 {
172   // Look through names table for one that matches
173   for (int i = 0; i < this->numEnums; i++) {
174     if (name == this->enumNames[i]) {
175       val = this->enumValues[i];
176       return TRUE;
177     }
178   }
179   return FALSE;
180 }
181 
182 /*!
183   Set the enumeration \a name which matches the given enumeration
184   value.
185 
186   Returns \c TRUE if \a value is a valid enumeration value, otherwise
187   \c FALSE.
188 */
189 SbBool
findEnumName(int valuearg,const SbName * & name) const190 SoSFEnum::findEnumName(int valuearg, const SbName *& name) const
191 {
192   // Look through values table for one that matches
193   for (int i = 0; i < this->numEnums; i++) {
194     if (valuearg == this->enumValues[i]) {
195       name = &(this->enumNames[i]);
196       return TRUE;
197     }
198   }
199   return FALSE;
200 }
201 
202 // No need to document readValue() and writeValue() here, as the
203 // necessary information is provided by the documentation of the
204 // parent classes.
205 #ifndef DOXYGEN_SKIP_THIS
206 
207 SbBool
readValue(SoInput * in)208 SoSFEnum::readValue(SoInput * in)
209 {
210   SbName n;
211   int val;
212 
213   // Read mnemonic value as a character string identifier
214   if (!in->read(n, TRUE)) {
215     // If we don't have any legal values for this field,
216     // give some slack and accept integer values.
217     if (this->legalValuesSet || !in->read(val)) {
218       SoReadError::post(in, "Couldn't read enumeration name");
219       return FALSE;
220     }
221   }
222   else {
223     if (!this->findEnumValue(n, val)) {
224       // If we read an enum field written by an extension node,
225       // we won't have any defined enum values. This is indicated by
226       // this->legalValuesSet == FALSE. If this is the case, define
227       // any encountered enum values on the fly but keep legalValuesSet
228       // to FALSE in order not to fool built-in enum field to accept
229       // illegal values.
230       if (!this->legalValuesSet) {
231         int *newvalues = new int[this->numEnums+1];
232         SbName *newnames = new SbName[this->numEnums+1];
233         int i;
234         for (i = 0; i < this->numEnums; i++) {
235           newvalues[i] = this->enumValues[i];
236           newnames[i] = this->enumNames[i];
237         }
238         newvalues[i] = i;
239         newnames[i] = n;
240         delete[] this->enumValues;
241         delete[] this->enumNames;
242         this->enumValues = newvalues;
243         this->enumNames = newnames;
244         this->numEnums += 1;
245         val = i;
246       }
247       else {
248         SoReadError::post(in, "Unknown enumeration value \"%s\"", n.getString());
249         return FALSE;
250       }
251     }
252   }
253   this->value = val;
254   return TRUE;
255 }
256 
257 void
writeValue(SoOutput * out) const258 SoSFEnum::writeValue(SoOutput * out) const
259 {
260   const SbName *enumname;
261   if (findEnumName(this->getValue(), enumname)) {
262     out->write(const_cast<char *>(enumname->getString()));
263     return;
264   }
265   // If we don't have any legal values for this field,
266   // pass through read integer values.
267   if (!this->legalValuesSet) {
268     out->write(this->getValue());
269     return;
270   }
271 
272 #if COIN_DEBUG
273   // An unknown enumeration value will usually be an indication of a
274   // more serious bug, so we elaborate a bit on the error to aid early
275   // debugging.  mortene.
276 
277   SbName name;
278   const SoFieldContainer * thecontainer = this->getContainer();
279   const SbBool fname = thecontainer && thecontainer->getFieldName(this, name);
280   SbString s("");
281   if (fname) { s.sprintf(" \"%s\"", name.getString()); }
282 
283   SoDebugError::post("SoSFEnum::writeValue",
284                      "Illegal enumeration value %d in field%s",
285                      this->getValue(), s.getString());
286 #endif // COIN_DEBUG
287 }
288 
289 /*!
290   Compare this field with \a f and return \c TRUE if they are
291   equal.
292 */
293 int
operator ==(const SoSFEnum & f) const294 SoSFEnum::operator==(const SoSFEnum & f) const
295 {
296   // Check for value mismatch first.
297   if (this->getValue() != f.getValue()) return FALSE;
298 
299   // Check that we have the same enumeration mappings.
300   if (this->numEnums != f.numEnums) return FALSE;
301   for (int i = 0; i < this->numEnums; i++) {
302     if (this->enumValues[i] != f.enumValues[i]) return FALSE;
303     if (this->enumNames[i] != f.enumNames[i]) return FALSE;
304   }
305 
306   return TRUE;
307 }
308 
309 /*!
310   Set the value of this field by specifying an enumeration integer value.
311 */
312 void
setValue(int newvalue)313 SoSFEnum::setValue(int newvalue)
314 {
315   this->value = newvalue;
316   this->valueChanged();
317 }
318 
319 #endif // DOXYGEN_SKIP_THIS
320 
321 /*!
322   Set the value of this field by specifying an enumeration string value.
323 */
324 void
setValue(const SbName name)325 SoSFEnum::setValue(const SbName name)
326 {
327   int val;
328   if (this->findEnumValue(name, val)) {
329     this->setValue(val);
330   }
331   else {
332 #if COIN_DEBUG
333     SoDebugError::post("SoSFEnum::setValue",
334                        "Unknown enum '%s'", name.getString());
335 #endif // COIN_DEBUG
336   }
337 }
338 
339 /*!
340   Returns the number of enum names the SoSFEnum object understands.
341 
342   Note that this API method is not part of the original SGI Inventor
343   2.1 API, and is an extension specific to Coin.
344 
345   \COIN_FUNCTION_EXTENSION
346 
347   \since Coin 2.0
348 */
349 int
getNumEnums(void) const350 SoSFEnum::getNumEnums(void) const
351 {
352   return this->numEnums;
353 }
354 
355 /*!
356   Returns the value of the Nth enum this SoSFEnum object understands,
357   and mutates \a name to contain the Nth enum's name.
358 
359   Note that this API method is not part of the original SGI Inventor
360   2.1 API, and is an extension specific to Coin.
361 
362   \COIN_FUNCTION_EXTENSION
363 
364   \since Coin 2.0
365 */
366 int
getEnum(const int idx,SbName & name) const367 SoSFEnum::getEnum(const int idx, SbName & name) const
368 {
369   if ( COIN_DEBUG && (idx < 0 || idx >= this->numEnums) ) {
370     SoDebugError::post("SoSFEnum::getEnum", "idx (%d) out of range", idx);
371     return -1;
372   }
373   name = this->enumNames[idx];
374   return this->enumValues[idx];
375 }
376 
377 #ifdef COIN_TEST_SUITE
378 
BOOST_AUTO_TEST_CASE(initialized)379 BOOST_AUTO_TEST_CASE(initialized)
380 {
381   SoSFEnum field;
382   BOOST_CHECK_MESSAGE(SoSFEnum::getClassTypeId() != SoType::badType(),
383                       "SoSFEnum class not initialized");
384   BOOST_CHECK_MESSAGE(field.getTypeId() != SoType::badType(),
385                       "missing class initialization");
386 }
387 
388 #endif // COIN_TEST_SUITE
389