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