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 SoEngineOutput SoEngine.h Inventor/engines/SoEngine.h
35   \brief The SoEngineOutput class is the output slots in SoEngine instances.
36 
37   \ingroup engines
38 
39   SoEngineOutput has methods for convenient handling of the
40   connections made from SoEngine objects to SoField objects.
41 
42   \sa SoEngine, SoField
43  */
44 
45 #include <Inventor/engines/SoEngineOutput.h>
46 
47 #include "SbBasicP.h"
48 
49 #include <Inventor/engines/SoEngine.h>
50 #include <Inventor/engines/SoNodeEngine.h>
51 #include <Inventor/engines/SoOutputData.h>
52 #include <Inventor/engines/SoFieldConverter.h>
53 #include <Inventor/fields/SoField.h>
54 #include <Inventor/misc/SoProtoInstance.h>
55 
56 #if COIN_DEBUG
57 #include <Inventor/errors/SoDebugError.h>
58 #endif // COIN_DEBUG
59 
60 /*!
61   Constructor. The SoEngineOutput will initially not be contained
62   within an SoEngine nor will it have any slave fields attached.
63 
64   \sa setContainer()
65 */
SoEngineOutput(void)66 SoEngineOutput::SoEngineOutput(void)
67 {
68   this->enabled = TRUE;
69   this->container = NULL;
70 }
71 
72 /*!
73   Destructor.
74 */
~SoEngineOutput()75 SoEngineOutput::~SoEngineOutput()
76 {
77 #if COIN_DEBUG && 0 // debug
78   SoDebugError::postInfo("SoEngineOutput::~SoEngineOutput", "start %p", this);
79 #endif // debug
80 
81   // Avoids evaluation from the fields in SoField::disconnect() (which
82   // would again lead to problems with the pure virtual
83   // SoEngine::evaluate() function during destruction of our
84   // container).
85   this->enabled = FALSE;
86 
87   // Disconnect all fields.
88   SoFieldList fl;
89   int nr = this->getForwardConnections(fl);
90   for (int i=0; i < nr; i++) fl[i]->disconnect(this);
91 
92 #if COIN_DEBUG && 0 // debug
93   SoDebugError::postInfo("SoEngineOutput::~SoEngineOutput", "done %p", this);
94 #endif // debug
95 }
96 
97 /*!
98   Returns the type of the engine output.
99 */
100 SoType
getConnectionType(void) const101 SoEngineOutput::getConnectionType(void) const
102 {
103   assert(this->container != NULL);
104   const SoEngineOutputData * outputs =
105     this->isNodeEngineOutput() ?
106     this->getNodeContainer()->getOutputData() :
107     this->getContainer()->getOutputData();
108   assert(outputs);
109   int idx = outputs->getIndex(this->container, this);
110   assert(idx >= 0 && "an engine should have at least one output");
111   return outputs->getType(idx);
112 }
113 
114 /*!
115   Adds all slave fields connected to this output to \a fl.  Returns
116   the number of slaves.
117 
118   \sa addConnection()
119   \sa removeConnection()
120 */
121 int
getForwardConnections(SoFieldList & fl) const122 SoEngineOutput::getForwardConnections(SoFieldList & fl) const
123 {
124   int numc = 0;
125   for (int i = 0; i < this->slaves.getLength(); i++) {
126     SoField * field = this->slaves[i];
127     SoFieldContainer * fc = field->getContainer();
128     // test for and skip field converters. Get the real field this
129     // output is connected to.
130     if (fc && fc->isOfType(SoFieldConverter::getClassTypeId())) {
131       SoFieldConverter * converter = coin_assert_cast<SoFieldConverter *>(fc);
132       numc += converter->getForwardConnections(fl);
133     }
134     else {
135       fl.append(field);
136       numc++;
137     }
138   }
139   return numc;
140 }
141 
142 /*!
143   Sets the enabled flag. If output is disabled, the fields connected
144   to this output will not be changed when the engine is evaluated.
145 
146   \sa isEnabled().
147 */
148 void
enable(const SbBool flag)149 SoEngineOutput::enable(const SbBool flag)
150 {
151   this->enabled = flag;
152   if (flag) this->touchSlaves(NULL, FALSE);
153 }
154 
155 /*!
156   Returns status of the enabled flag.
157 
158   \sa enable().
159 */
160 SbBool
isEnabled(void) const161 SoEngineOutput::isEnabled(void) const
162 {
163   return this->enabled;
164 }
165 
166 /*!
167   Returns the engine containing this output. If the engine
168   containing this output is a NodeEngine, this method returns NULL.
169 
170   \sa setContainer(), getNodeContainer()
171 */
172 SoEngine *
getContainer(void) const173 SoEngineOutput::getContainer(void) const
174 {
175   if (this->isNodeEngineOutput()) {
176 #if COIN_DEBUG
177     SoDebugError::postWarning("SoEngineOutput::getContainer",
178                               "Container is not an Engine");
179 #endif // COIN_DEBUG
180     return NULL;
181   }
182   return this->container;
183 }
184 
185 /*!
186 
187   Returns the node engine containing this output. If the engine
188   containing this output is not a NodeEgine, this method returns NULL.
189 
190   \COIN_FUNCTION_EXTENSION
191 
192   \sa setNodeContainer(), getContainer()
193   \since Coin 2.0
194 */
195 
196 SoNodeEngine *
getNodeContainer(void) const197 SoEngineOutput::getNodeContainer(void) const
198 {
199   if (!this->isNodeEngineOutput()) {
200 #if COIN_DEBUG
201     SoDebugError::postWarning("SoEngineOutput::getContainer",
202                               "Container is not a NodeEngine");
203 #endif // COIN_DEBUG
204     return NULL;
205   }
206   return coin_assert_cast<SoNodeEngine*>(this->container);
207 }
208 
209 /*!
210   Returns \e TRUE if the container is a NodeEngine.
211 
212   \COIN_FUNCTION_EXTENSION
213 
214   \sa getNodeContainer(), getContainer()
215   \since Coin 2.0
216 */
217 SbBool
isNodeEngineOutput(void) const218 SoEngineOutput::isNodeEngineOutput(void) const
219 {
220   assert(this->container);
221   return this->container->getTypeId().isDerivedFrom(SoNodeEngine::getClassTypeId());
222 }
223 
224 /*!
225   Sets the engine containing this output.
226 
227   \sa getContainer()
228 */
229 void
setContainer(SoEngine * engine)230 SoEngineOutput::setContainer(SoEngine * engine)
231 {
232   this->container = engine;
233 }
234 
235 
236 /*!
237   Sets the NodeEngine containing this output.
238 
239   \COIN_FUNCTION_EXTENSION
240 
241   \sa getNodeContainer()
242   \since Coin 2.0
243 */
244 
245 void
setNodeContainer(SoNodeEngine * nodeengine)246 SoEngineOutput::setNodeContainer(SoNodeEngine * nodeengine)
247 {
248   // FIXME: hack cast to SoEngine. The type of the container member
249   // needs to be SoFieldContainer, not SoEngine.
250   this->container = reinterpret_cast<SoEngine*> (nodeengine);
251 }
252 
253 /*!
254   Adds \a f to the list of connections from this output.
255 
256   Adds 1 to the reference count of the container engine.
257 
258   \sa removeConnection(), getForwardConnections()
259 */
260 void
addConnection(SoField * f)261 SoEngineOutput::addConnection(SoField * f)
262 {
263 #if COIN_DEBUG
264   if (this->slaves.find(f) != -1) {
265     SoDebugError::postWarning("SoEngineOutput::addConnection",
266                               "connection from %p already made", f);
267     return;
268   }
269 #endif // COIN_DEBUG
270 
271   this->slaves.append(f);
272 
273   // An engine's reference count increases with the number of
274   // connections it has.
275 
276   // SoProtoInstance has some special memory handling. Don't ref
277   // and unref if the connection is to an SoProtoInstance
278   SoFieldContainer * fc = f->getContainer();
279 
280   if (fc && !fc->isOfType(SoProtoInstance::getClassTypeId()) &&
281 	  // don't reference if the slave and master are in the same container
282 	  // else the corresponding unref can never happen RHW
283 	  fc != this->getFieldContainer ()) {
284     this->getFieldContainer()->ref();
285   }
286 
287   // Trigger re-evaluation of engine.
288   if (!this->isNodeEngineOutput()) {
289     this->getContainer()->setDirty();
290   }
291 }
292 
293 /*!
294   Removes \a f from the list of connections from this output.
295 
296   Subtracts 1 from the reference count of the container engine. If the
297   reference count reaches zero (which will happen if this was the last
298   connection and the application programmer did not explicitly \e ref
299   the engine), the container engine will be deallocated.
300 
301   \sa addConnection(), getForwardConnections()
302 */
303 void
removeConnection(SoField * f)304 SoEngineOutput::removeConnection(SoField * f)
305 {
306   int i = this->slaves.find(f);
307 #if COIN_DEBUG
308   if (i == -1) {
309     SoDebugError::postWarning("SoEngineOutput::removeConnection",
310                               "no connection from %p present", f);
311     return;
312   }
313 #endif // COIN_DEBUG
314   this->slaves.remove(i);
315 
316   // SoProtoInstance has some special memory handling. Don't ref
317   // and unref if the connection is to an SoProtoInstance
318   SoFieldContainer * fc = f->getContainer();
319 
320   if (fc && !fc->isOfType(SoProtoInstance::getClassTypeId()) &&
321 	  // we don't want to unref when connected to self RHW
322 	  fc != this->getFieldContainer ()) {
323     this->getFieldContainer()->unref();
324   }
325 }
326 
327 /*!
328   Returns the number of fields in the list of connections.
329 
330   \sa operator[], addConnection()
331 */
332 int
getNumConnections(void) const333 SoEngineOutput::getNumConnections(void) const
334 {
335   return this->slaves.getLength();
336 }
337 
338 /*!
339   Returns the field at index \a i in the list of connections.
340 
341   Please note that this might not be the same field as the one the
342   output was originally connected to, since a field converted might
343   have been inserted to converted the output to match the field type.
344 
345   \sa getNumConnections()
346 */
347 SoField *
operator [](int i) const348 SoEngineOutput::operator[](int i) const
349 {
350   return this->slaves[i];
351 }
352 
353 /*!
354   Disables notifications on fields connected to this output.  This is
355   done before the engine is evaulated, since the fields we are going
356   to write into have already been notified.
357 
358   \sa doneWriting()
359 */
360 void
prepareToWrite(void) const361 SoEngineOutput::prepareToWrite(void) const
362 {
363   SoEngineOutput * that = const_cast<SoEngineOutput *>(this);
364   that->fieldnotiflist.truncate(0);
365 
366   int n = this->slaves.getLength();
367   for (int i = 0; i < n; i++) {
368     SoField * f = this->slaves[i];
369     that->fieldnotiflist.append(f->isNotifyEnabled());
370     f->enableNotify(FALSE);
371   }
372 }
373 
374 /*!
375   Restores the notification flags on fields connected to this output
376   after evaluating.
377 
378   \sa prepareToWrite()
379 */
380 void
doneWriting(void) const381 SoEngineOutput::doneWriting(void) const
382 {
383   // We should have the exact same set of slave fields now as on entry
384   // to prepareToWrite(), as the field writing is supposed be a
385   // "closed" operation.  (All notifications on the fields are
386   // disabled, so no application code should be able to intervene in
387   // SoEngine evaluation.)
388 
389   int n = this->slaves.getLength();
390   assert(n == this->fieldnotiflist.getLength());
391 
392   const SbBool * notifs = this->fieldnotiflist.getArrayPtr();
393 
394   for (int i = 0; i < n; i++) {
395     this->slaves[i]->enableNotify(notifs[i]);
396   }
397 }
398 
399 /*!
400   Notify the fields attached to this engine output that the output
401   value has changed.
402 
403   If \a donotify is \c TRUE, propagate the notification to the
404   fields. Otherwise just mark the fields "dirty" for re-evalution.
405 
406   Note that this method is not part of the original Open Inventor API.
407  */
408 void
touchSlaves(SoNotList * nl,SbBool donotify)409 SoEngineOutput::touchSlaves(SoNotList * nl, SbBool donotify)
410 {
411   if (this->isEnabled()) {
412     // use a copy of the notification list so that the connections are
413     // not added to the original list
414     SoNotList listcopy;
415     if (nl && donotify) listcopy = *nl;
416     int numconnections = this->getNumConnections();
417     for (int j = 0; j < numconnections; j++) {
418       SoField * field = (*this)[j];
419       if (donotify) {
420         field->notify(&listcopy);
421         if (j < numconnections-1) {
422           listcopy = *nl;
423         }
424       }
425       else field->setDirty(TRUE);
426     }
427   }
428 }
429 
430 /*!
431   Convenience method that returns a field container. This method can
432   be used both for NodeEngine and Engine outputs.
433 
434   \COIN_FUNCTION_EXTENSION
435 
436   \since Coin 2.0
437 */
438 SoFieldContainer *
getFieldContainer(void)439 SoEngineOutput::getFieldContainer(void)
440 {
441   return coin_assert_cast<SoFieldContainer *>(this->container);
442 }
443