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 SoSelectOne SoSelectOne.h Inventor/engines/SoSelectOne.h
35 \brief The SoSelectOne class is used to select one value from a set of values.
36
37 \ingroup engines
38
39 The output field will be the index'th value of the input multivalue
40 field.
41
42
43 Note that this engine's output field deviates a little from the
44 "standard" output mechanism of the majority of engine classes: the
45 SoSelectOne::output is not a permanent SoEngineOutput instance, but
46 a \e pointer to a SoEngineOutput instance. The reason for this is
47 that it is necessary to allocate the output field dynamically to
48 make it match what the SoSelectOne::input is connected to since the
49 type of the SoSelectOne::output always should be the same as the
50 type of the SoSelectOne::input.
51
52
53 \ENGINE_TYPELESS_FILEFORMAT
54
55 \verbatim
56 SelectOne {
57 type <multivaluefieldtype>
58 [...fields...]
59 }
60 \endverbatim
61 */
62
63 #include <Inventor/engines/SoSelectOne.h>
64
65 #include <cstring>
66
67 #include <Inventor/SoInput.h>
68 #include <Inventor/SoOutput.h>
69 #include <Inventor/engines/SoEngineOutput.h>
70 #include <Inventor/errors/SoReadError.h>
71 #include <Inventor/fields/SoFields.h>
72 #include <Inventor/lists/SoEngineOutputList.h>
73
74 #if COIN_DEBUG
75 #include <Inventor/errors/SoDebugError.h>
76 #endif // COIN_DEBUG
77
78 #include "engines/SoSubEngineP.h"
79 #include "SbBasicP.h"
80 #include "coindefs.h"
81
82 #ifndef COIN_WORKAROUND_NO_USING_STD_FUNCS
83 using std::strstr;
84 #endif // !COIN_WORKAROUND_NO_USING_STD_FUNCS
85
86 /*!
87 \var SoMField * SoSelectOne::input
88 The multivalue input field which we will select a single value from
89 for our output.
90 */
91 /*!
92 \var SoSFInt32 SoSelectOne::index
93 Index of the value from the input field which will be put on the
94 output.
95 */
96 /*!
97 \var SoEngineOutput * SoSelectOne::output
98 (SoSField) This is the singlevalue field output containing the index'th
99 value of SoSelectOne::input.
100
101 The type of the field will of course match the type of the input field,
102 i.e. if SoSelectOne::input is an SoMFFloat, SoSelectOne::output will
103 be an SoSFFloat etc.
104 */
105
106 // Can't use the standard SO_ENGINE_SOURCE macro, as this engine
107 // doesn't keep a class-global set of inputs and outputs: we need to
108 // make an instance of SoFieldData and SoEngineOutputData for every
109 // instance of the class, since the input and output fields are
110 // dynamically allocated.
111 SO_INTERNAL_ENGINE_SOURCE_DYNAMIC_IO(SoSelectOne);
112
113 static SbBool
SoSelectOne_valid_type(SoType t)114 SoSelectOne_valid_type(SoType t)
115 {
116 return (t.isDerivedFrom(SoMField::getClassTypeId()) &&
117 t.canCreateInstance());
118 }
119
120 /*!
121 Constructor. Sets the type of the input field. The input field must
122 be of type SoMField.
123 */
SoSelectOne(SoType inputtype)124 SoSelectOne::SoSelectOne(SoType inputtype)
125 {
126 this->dynamicinput = NULL;
127 this->dynamicoutput = NULL;
128 this->input = NULL;
129 this->output = NULL;
130
131 #if COIN_DEBUG
132 if (!SoSelectOne_valid_type(inputtype)) {
133 SoDebugError::post("SoSelectOne::SoSelectOne",
134 "invalid type '%s' for input field, "
135 "field must be non-abstract and a multi-value type.",
136 inputtype == SoType::badType() ? "badType" :
137 inputtype.getName().getString());
138 }
139 #endif // COIN_DEBUG
140
141 this->initialize(inputtype);
142 }
143
144 // Default constructor. Leaves engine in invalid state. Should only be
145 // used from import code or copy code.
SoSelectOne(void)146 SoSelectOne::SoSelectOne(void)
147 {
148 this->dynamicinput = NULL;
149 this->dynamicoutput = NULL;
150 this->input = NULL;
151 this->output = NULL;
152 }
153
154 // Set up the input and output fields of the engine. This is done from
155 // either the non-default constructor or the readInstance() import
156 // code.
157 void
initialize(const SoType inputfieldtype)158 SoSelectOne::initialize(const SoType inputfieldtype)
159 {
160 assert(this->input == NULL);
161 assert(SoSelectOne_valid_type(inputfieldtype));
162
163 SO_ENGINE_INTERNAL_CONSTRUCTOR(SoSelectOne);
164 SO_ENGINE_ADD_INPUT(index, (0));
165
166 // Instead of SO_ENGINE_ADD_INPUT().
167 this->input = static_cast<SoMField *>(inputfieldtype.createInstance());
168 this->input->setNum(0);
169 this->input->setContainer(this);
170 this->dynamicinput = new SoFieldData(SoSelectOne::inputdata);
171 this->dynamicinput->addField(this, "input", this->input);
172
173 SbString multiname = inputfieldtype.getName().getString();
174 // Built-in fields always start with the "MF", but we try to handle
175 // user-defined fields aswell.
176 const char * ptr = strstr(multiname.getString(), "MF");
177 assert(ptr != NULL && "invalid input field type");
178 const ptrdiff_t offset = ptr - multiname.getString();
179 SbString singlename = (offset == 0) ? SbString("") : multiname.getSubString(0, int(offset - 1));
180 singlename += 'S';
181 singlename += multiname.getSubString(int(offset + 1));
182
183 SoType outputtype = SoType::fromName(singlename);
184 assert(outputtype != SoType::badType() &&
185 outputtype.isDerivedFrom(SoSField::getClassTypeId()) &&
186 "invalid input field type");
187
188 // Instead of SO_ENGINE_ADD_OUTPUT().
189 this->output = new SoEngineOutput;
190 this->dynamicoutput = new SoEngineOutputData(SoSelectOne::outputdata);
191 this->dynamicoutput->addOutput(this, "output", this->output, outputtype);
192 this->output->setContainer(this);
193 }
194
195 // Documented in superclass.
196 void
initClass(void)197 SoSelectOne::initClass(void)
198 {
199 SO_ENGINE_INTERNAL_INIT_CLASS(SoSelectOne);
200 }
201
~SoSelectOne()202 SoSelectOne::~SoSelectOne()
203 {
204 delete this->dynamicinput;
205 delete this->dynamicoutput;
206
207 delete this->input;
208 delete this->output;
209 }
210
211 // Documented in superclass.
212 void
evaluate(void)213 SoSelectOne::evaluate(void)
214 {
215 int idx = this->index.getValue();
216
217 if (idx == 0 && this->input->getNum() == 0) {
218 // Nil is the no-op value (also the default initial value).
219 SO_ENGINE_OUTPUT((*output), SoSField, setDirty(FALSE));
220 }
221 else if (idx >= 0 && idx < this->input->getNum()) {
222
223 // Macro used to generate the right casts for copying field values
224 #define IF_TYPE(_var_, _fieldtype_) \
225 if(_var_ == SoMF##_fieldtype_::getClassTypeId() ) \
226 { \
227 SO_ENGINE_OUTPUT((*output), SoSF##_fieldtype_, setValue((*(coin_assert_cast<SoMF##_fieldtype_ *>(this->input)))[idx])); \
228 }
229 // end of macro
230
231 SoType type = this->input->getTypeId();
232 IF_TYPE(type,BitMask)
233 else IF_TYPE(type,Bool)
234 else IF_TYPE(type,Color)
235 else IF_TYPE(type,Engine)
236 else IF_TYPE(type,Enum)
237 else IF_TYPE(type,Float)
238 else IF_TYPE(type,Int32)
239 else IF_TYPE(type,Matrix)
240 else IF_TYPE(type,Name)
241 else IF_TYPE(type,Node)
242 else IF_TYPE(type,Path)
243 else IF_TYPE(type,Plane)
244 else IF_TYPE(type,Rotation)
245 else IF_TYPE(type,Short)
246 else IF_TYPE(type,String)
247 else IF_TYPE(type,Time)
248 else IF_TYPE(type,UInt32)
249 else IF_TYPE(type,UShort)
250 else IF_TYPE(type,Vec2f)
251 else IF_TYPE(type,Vec3f)
252 else IF_TYPE(type,Vec4f)
253 else {
254 // fall back for user defined types, and built-in types not
255 // covered by the above (if any)
256 SbString valuestring;
257 this->input->get1(idx, valuestring);
258 SO_ENGINE_OUTPUT((*output), SoSField, set(valuestring.getString()));
259 }
260 #undef IF_TYPE
261 }
262 #if COIN_DEBUG
263 else {
264 SoDebugError::post("SoSelectOne::evaluate", "invalid index %d", idx);
265 }
266 #endif // COIN_DEBUG
267 }
268
269 // Documented in superclass.
270 SbBool
readInstance(SoInput * in,unsigned short flagsarg)271 SoSelectOne::readInstance(SoInput * in, unsigned short flagsarg)
272 {
273 // This code is identical to readInstance() of SoGate and
274 // SoConcatenate, so migrate changes.
275
276 SbName tmp;
277 if (!in->read(tmp) || tmp != "type") {
278 SoReadError::post(in,
279 "\"type\" keyword is missing, erroneous format for "
280 "engine class '%s'.",
281 this->getTypeId().getName().getString());
282 return FALSE;
283 }
284 // need to use an SbString here, because SoInput::read( SbName & )
285 // reads in '"MyName"' as is instead of as 'MyName'.
286 SbString fieldname;
287 if (!in->read(fieldname)) {
288 SoReadError::post(in, "Couldn't read input type for engine.");
289 return FALSE;
290 }
291 SoType inputtype = SoType::fromName(fieldname);
292 if (!SoSelectOne_valid_type(inputtype)) {
293 SoReadError::post(in, "Type \"%s\" for input field is not valid "
294 "(field must be non-abstract and a multi-value type).",
295 fieldname.getString());
296 return FALSE;
297 }
298
299 this->initialize(inputtype);
300 return SoEngine::readInstance(in, flagsarg);
301 }
302
303 // Documented in superclass.
304 void
writeInstance(SoOutput * out)305 SoSelectOne::writeInstance(SoOutput * out)
306 {
307 // This code is identical to writeInstance() of SoGate and
308 // SoConcatenate, so migrate changes.
309
310 if (this->writeHeader(out, FALSE, TRUE)) return;
311
312 SbBool binarywrite = out->isBinary();
313
314 if (!binarywrite) out->indent();
315 out->write("type");
316 if (!binarywrite) out->write(' ');
317 out->write(this->input->getTypeId().getName());
318 if (binarywrite) out->write(static_cast<unsigned int>(0));
319 else out->write('\n');
320
321 this->getFieldData()->write(out, this);
322 this->writeFooter(out);
323 }
324
325 // Documented in superclass.
326 void
copyContents(const SoFieldContainer * from,SbBool copyconnections)327 SoSelectOne::copyContents(const SoFieldContainer * from,
328 SbBool copyconnections)
329 {
330 const SoSelectOne * selectonesrc = coin_assert_cast<const SoSelectOne *>(from);
331 if (selectonesrc->input) { this->initialize(selectonesrc->input->getTypeId()); }
332 inherited::copyContents(from, copyconnections);
333 }
334