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