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