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 SoMFEnum SoMFEnum.h Inventor/fields/SoMFEnum.h
35   \brief The SoMFEnum class is a container for a set of enumerated values.
36 
37   \ingroup fields
38 
39   This field is used where nodes, engines or other field containers
40   needs to store values constrained to be from an enumerated set.
41 
42   A field of this type stores its values to file as the symbolic
43   names, rather than the actual integer values.
44 
45   \sa SoSFEnum, SoMFBitMask
46 */
47 
48 // *************************************************************************
49 
50 #include <Inventor/fields/SoMFEnum.h>
51 
52 #include <cassert>
53 
54 #include <Inventor/fields/SoFieldContainer.h>
55 #include <Inventor/errors/SoReadError.h>
56 #include <Inventor/errors/SoDebugError.h>
57 #include <Inventor/SoInput.h>
58 #include <Inventor/SoOutput.h>
59 
60 #include "fields/SoSubFieldP.h"
61 
62 // *************************************************************************
63 
64 /*!
65   \var int SoMFEnum::numEnums
66   Number of enumeration mappings.
67 */
68 /*!
69   \var SbName * SoMFEnum::enumNames
70   Array of enumeration names. Maps 1-to-1 with the enumValues.
71 */
72 /*!
73   \var int * SoMFEnum::enumValues
74   Array of enumeration values. Maps 1-to-1 with the enumNames.
75 */
76 /*!
77   \var SbBool SoMFEnum::legalValuesSet
78   Is \c TRUE if a set of enum name-to-value mappings has been set.
79 */
80 
81 // *************************************************************************
82 
83 SO_MFIELD_REQUIRED_SOURCE(SoMFEnum);
84 SO_MFIELD_MALLOC_SOURCE(SoMFEnum, int);
85 SO_MFIELD_VALUE_SOURCE(SoMFEnum, int, int);
86 
87 // *************************************************************************
88 
89 // (Declarations hidden in SO_[S|M]FIELD_HEADER macro in header file,
90 // so don't use Doxygen commenting.)
91 #ifndef DOXYGEN_SKIP_THIS
92 
93 /* Constructor. */
SoMFEnum(void)94 SoMFEnum::SoMFEnum(void)
95 {
96   this->values = NULL;
97   this->legalValuesSet = FALSE;
98   this->numEnums = 0;
99   this->enumValues = NULL;
100   this->enumNames = NULL;
101 }
102 
103 /* Destructor. */
~SoMFEnum()104 SoMFEnum::~SoMFEnum()
105 {
106   this->enableNotify(FALSE); /* Avoid notifying destructed containers. */
107   this->deleteAllValues();
108   delete[] this->enumValues;
109   delete[] this->enumNames;
110 }
111 
112 #endif // DOXYGEN_SKIP_THIS
113 
114 
115 // Override from parent class.
116 void
initClass(void)117 SoMFEnum::initClass(void)
118 {
119   SO_MFIELD_INTERNAL_INIT_CLASS(SoMFEnum);
120 }
121 
122 
123 // No need to document readValue() and writeValue() here, as the
124 // necessary information is provided by the documentation of the
125 // parent classes.
126 #ifndef DOXYGEN_SKIP_THIS
127 
128 SbBool
read1Value(SoInput * in,int idx)129 SoMFEnum::read1Value(SoInput * in, int idx)
130 {
131   SbName n;
132   int val;
133 
134   // Read mnemonic value as a character string identifier
135   if (!in->read(n, TRUE)) {
136     // If we don't have any legal values for this field,
137     // give some slack and accept integer values.
138     if (this->legalValuesSet || !in->read(val)) {
139       SoReadError::post(in, "Couldn't read enumeration name");
140       return FALSE;
141     }
142   }
143   else {
144     if (!this->findEnumValue(n, val)) {
145       // If we read an enum field written by an extension node,
146       // we won't have any defined enum values. This is indicated by
147       // this->legalValuesSet == FALSE. If this is the case, define
148       // any encountered enum values on the fly but keep legalValuesSet
149       // to FALSE in order not to fool built-in enum field to accept
150       // illegal values.
151       if (!this->legalValuesSet) {
152         int *newvalues = new int[this->numEnums+1];
153         SbName *newnames = new SbName[this->numEnums+1];
154         int i;
155         for (i = 0; i < this->numEnums; i++) {
156           newvalues[i] = this->enumValues[i];
157           newnames[i] = this->enumNames[i];
158         }
159         newvalues[i] = i;
160         newnames[i] = n;
161         delete[] this->enumValues;
162         delete[] this->enumNames;
163         this->enumValues = newvalues;
164         this->enumNames = newnames;
165         this->numEnums += 1;
166         val = i;
167       }
168       else {
169         SoReadError::post(in, "Unknown enumeration value \"%s\"", n.getString());
170         return FALSE;
171       }
172     }
173   }
174 
175   assert(idx < this->maxNum);
176   this->values[idx] = val;
177   return TRUE;
178 }
179 
180 // FIXME: this should share code with SoSFEnum::writeValue(). 20031205 mortene.
181 void
write1Value(SoOutput * out,int idx) const182 SoMFEnum::write1Value(SoOutput * out, int idx) const
183 {
184   int val = (*this)[idx];
185   const SbName *enumname;
186   if (findEnumName(val, enumname)) {
187     out->write(const_cast<char *>(enumname->getString()));
188     return;
189   }
190   // If we don't have any legal values for this field,
191   // pass through read integer values.
192   if (!this->legalValuesSet) {
193     out->write(val);
194     return;
195   }
196 
197 #if COIN_DEBUG
198   SoDebugError::post("SoMFEnum::writeValue", "Illegal value (%d) in field", val);
199 #endif // COIN_DEBUG
200 }
201 
202 #endif // DOXYGEN_SKIP_THIS
203 
204 
205 /*!
206   Set this field to contain a single value by specifying an
207   enumeration string.
208 */
209 void
setValue(const SbName name)210 SoMFEnum::setValue(const SbName name)
211 {
212   int val;
213   if (this->findEnumValue(name, val)) {
214     this->setValue(val);
215   }
216 #if COIN_DEBUG
217   else {
218     SoDebugError::post("SoMFEnum::setValue",
219                        "Unknown enum '%s'", name.getString());
220   }
221 #endif // COIN_DEBUG
222 }
223 
224 /*!
225   Set the value at \a idx to the enumeration value represented
226   by \a name.
227 */
228 void
set1Value(const int idx,const SbName name)229 SoMFEnum::set1Value(const int idx, const SbName name)
230 {
231   int val;
232   if(this->findEnumValue(name, val)) {
233     this->set1Value(idx, val);
234   }
235 #if COIN_DEBUG
236   else {
237     SoDebugError::post("SoMFEnum::setValue",
238                        "Unknown enum '%s'", name.getString());
239   }
240 #endif // COIN_DEBUG
241 }
242 
243 /*!
244   Makes a set of \a num enumeration \a names map to \a vals.
245 */
246 void
setEnums(const int numarg,const int * const vals,const SbName * const names)247 SoMFEnum::setEnums(const int numarg, const int * const vals,
248                     const SbName * const names)
249 {
250   delete[] this->enumValues;
251   delete[] this->enumNames;
252 
253   this->enumValues = new int[numarg];
254   this->enumNames = new SbName[numarg];
255   this->numEnums = numarg;
256   this->legalValuesSet = TRUE;
257 
258   for (int i = 0; i < this->numEnums; i++) {
259     this->enumValues[i] = vals[i];
260     this->enumNames[i] = names[i];
261   }
262 }
263 
264 /*!
265   Return in \a val the enumeration value which matches the given
266   enumeration \a name.
267 
268   Returns \c TRUE if \a name is a valid enumeration string, otherwise
269   \c FALSE.
270 */
271 SbBool
findEnumValue(const SbName & name,int & val)272 SoMFEnum::findEnumValue(const SbName & name, int & val)
273 {
274   // Look through names table for one that matches
275   for (int i = 0; i < this->numEnums; i++) {
276     if (name == this->enumNames[i]) {
277       val = this->enumValues[i];
278       return TRUE;
279     }
280   }
281   return FALSE;
282 }
283 
284 /*!
285   Set the enumeration \a name which matches the given enumeration
286   value.
287 
288   Returns \c TRUE if \a value is a valid enumeration value, otherwise
289   \c FALSE.
290 */
291 SbBool
findEnumName(int value,const SbName * & name) const292 SoMFEnum::findEnumName(int value, const SbName * & name) const
293 {
294   // Look through values table for one that matches
295   for (int i = 0; i < this->numEnums; i++) {
296     if (value == this->enumValues[i]) {
297       name = &(this->enumNames[i]);
298       return TRUE;
299     }
300   }
301   return FALSE;
302 }
303 
304 /*!
305   Returns the number of enum names the SoSFEnum object understands.
306 
307   \COIN_FUNCTION_EXTENSION
308 
309   \since Coin 2.0
310 */
311 int
getNumEnums(void) const312 SoMFEnum::getNumEnums(void) const
313 {
314   return this->numEnums;
315 }
316 
317 /*!
318   Returns the value of the Nth enum this SoSFEnum object understands,
319   and mutates \a name to contain the Nth enum's name.
320 
321   \COIN_FUNCTION_EXTENSION
322 
323   \since Coin 2.0
324 */
325 int
getEnum(const int idx,SbName & name) const326 SoMFEnum::getEnum(const int idx, SbName & name) const
327 {
328   if ( COIN_DEBUG && (idx < 0 || idx >= this->numEnums) ) {
329     SoDebugError::post("SoSFEnum::getEnum", "idx (%d) out of range", idx);
330     return -1;
331   }
332   name = this->enumNames[idx];
333   return this->enumValues[idx];
334 }
335 
336 
337 #ifdef COIN_TEST_SUITE
338 
BOOST_AUTO_TEST_CASE(initialized)339 BOOST_AUTO_TEST_CASE(initialized)
340 {
341   SoMFEnum field;
342   BOOST_CHECK_MESSAGE(field.getTypeId() != SoType::badType(),
343                       "missing class initialization");
344   BOOST_CHECK_EQUAL(field.getNum(), 0);
345 }
346 
347 #endif // COIN_TEST_SUITE
348