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 // Important note: this sourcefile was mostly generated by the
34 // Coin/scripts/templant script from the code in MFNodeEnginePath.tpl,
35 // only the notify() method is specific for SoMFPath.cpp.
36 
37 //$ BEGIN TEMPLATE MFNodeEnginePath(PATH, Path, path)
38 
39 /*!
40   \class SoMFPath SoMFPath.h Inventor/fields/SoMFPath.h
41   \brief The SoMFPath class is a container for paths.
42 
43   \ingroup fields
44 
45   This field container stores an array of pointers to paths. It takes
46   care of the necessary functionality for handling copy, import and
47   export operations.
48 
49   Note that path pointers stored in field instances of this type may
50   be \c NULL pointers.
51 
52   \sa SoPath, SoSFPath
53 
54 */
55 
56 // Type-specific define to be able to do #ifdef tests on type.  (Note:
57 // used to check the header file wrapper define, but that doesn't work
58 // with --enable-compact build.)
59 #define COIN_INTERNAL_SOMFPATH
60 
61 #include <Inventor/fields/SoMFPath.h>
62 
63 #include "coindefs.h"
64 #include "SbBasicP.h"
65 
66 #include <Inventor/fields/SoSFPath.h>
67 #include <Inventor/SoOutput.h>
68 #include <Inventor/actions/SoWriteAction.h>
69 #include <Inventor/SoPath.h>
70 #include <Inventor/engines/SoEngine.h>
71 #include <Inventor/nodes/SoNode.h>
72 #if COIN_DEBUG
73 #include <Inventor/errors/SoDebugError.h>
74 #endif // COIN_DEBUG
75 
76 #include "fields/SoSubFieldP.h"
77 
78 // These are the macros from SO_MFIELD_SOURCE_MALLOC we're
79 // using. What's missing is the SO_MFIELD_VALUE_SOURCE macro, which we
80 // need to implement "by hand" so reference counting and auditing
81 // comes out correctly.
82 SO_MFIELD_REQUIRED_SOURCE(SoMFPath);
83 SO_MFIELD_CONSTRUCTOR_SOURCE(SoMFPath);
84 SO_MFIELD_MALLOC_SOURCE(SoMFPath, SoPath *);
85 // Note that we're using the MALLOC versions (which just does
86 // bit-copying) of the macros, and not the the ALLOC versions (which
87 // allocates with "new", so constructors are run). The reason for this
88 // is that it's node/engine/path *pointers* that are simply bit-wise
89 // copied.
90 
91 
92 // Override from parent class.
93 void
initClass(void)94 SoMFPath::initClass(void)
95 {
96   SO_MFIELD_INTERNAL_INIT_CLASS(SoMFPath);
97 }
98 
99 
100 // No need to document readValue() and writeValue() here, as the
101 // necessary information is provided by the documentation of the
102 // parent classes.
103 #ifndef DOXYGEN_SKIP_THIS
104 
105 //// From the SO_MFIELD_VALUE_SOURCE macro, start. ///////////////////////////
106 
107 // We can't use the macro invocation, as we need to take care of doing
108 // ref() and unref() on the paths in the array.
109 
110 int
fieldSizeof(void) const111 SoMFPath::fieldSizeof(void) const
112 {
113   return sizeof(SoPath *);
114 }
115 
116 void *
valuesPtr(void)117 SoMFPath::valuesPtr(void)
118 {
119   return this->values;
120 }
121 
122 void
setValuesPtr(void * ptr)123 SoMFPath::setValuesPtr(void * ptr)
124 {
125   // We don't get any ref()'ing done here, or any notification
126   // mechanisms set up -- so this function should _only_ be used for
127   // initial setup of array memory.  In Coin, it's only used from
128   // SoMField::allocValues().
129   this->values = static_cast<SoPath **>(ptr);
130 }
131 
132 int
find(SoPath * value,SbBool addifnotfound)133 SoMFPath::find(SoPath * value, SbBool addifnotfound)
134 {
135   for (int i=0; i < this->num; i++) if ((*this)[i] == value) return i;
136 
137   if (addifnotfound) this->set1Value(this->num, value);
138   return -1;
139 }
140 
141 void
setValues(const int start,const int numarg,const SoPath ** newvals)142 SoMFPath::setValues(const int start, const int numarg, const SoPath ** newvals)
143 {
144   // Disable temporarily, so we under any circumstances will not send
145   // more than one notification about the changes.
146   SbBool notificstate = this->enableNotify(FALSE);
147   // Important note: the notification state is reset at the end, so
148   // this function should *not* have multiple return-points.
149 
150   // ref() new paths before unref()-ing old ones, in case there are
151   // common paths (we don't want any premature destruction to happen).
152   { for (int i=0; i < numarg; i++) if (newvals[i]) newvals[i]->ref(); }
153 
154   // We favor simplicity of code over performance here.
155   { for (int i=0; i < numarg; i++)
156     this->set1Value(start+i, const_cast<SoPath *>(newvals[i])); }
157 
158   // unref() to match the initial ref().
159   { for (int i=0; i < numarg; i++) if (newvals[i]) newvals[i]->unref(); }
160 
161   // Finally, send notification.
162   (void)this->enableNotify(notificstate);
163   this->setChangedIndices(start, numarg);
164   if (notificstate) this->valueChanged();
165   this->setChangedIndices();
166 }
167 
168 void
set1Value(const int idx,SoPath * newval)169 SoMFPath::set1Value(const int idx, SoPath * newval)
170 {
171   // Disable temporarily, so we under no circumstances will send more
172   // than one notification about the change.
173   SbBool notificstate = this->enableNotify(FALSE);
174   // Important note: the notification state is reset at the end, so
175   // this function should *not* have multiple return-points.
176 
177   // Don't use getNum(), getValues() or operator[] to find old values,
178   // since this might trigger a recursive evaluation call if the field
179   // is connected.
180 
181   // Expand array if necessary.
182   if (idx >= this->num) {
183 #ifdef COIN_INTERNAL_SOMFPATH
184     for (int i = this->num; i <= idx; i++) this->pathheads.append(NULL);
185 #endif // COIN_INTERNAL_SOMFPATH
186     this->setNum(idx + 1);
187   }
188 
189   SoPath * oldptr = this->values[idx];
190   if (oldptr != newval) {
191     if (oldptr) {
192 #ifdef COIN_INTERNAL_SOMFPATH
193       SoNode * h = oldptr->getHead();
194       // The path should be audited by us at all times. So don't use
195       // SoMFPath to wrap SoTempPath or SoLightPath, for instance.
196       assert(h==this->pathheads[idx] &&
197              "Path head changed without notification!");
198       if (h) {
199         h->removeAuditor(this, SoNotRec::FIELD);
200         h->unref();
201       }
202 #endif // COIN_INTERNAL_SOMFPATH
203       oldptr->removeAuditor(this, SoNotRec::FIELD);
204       oldptr->unref();
205     }
206 
207     if (newval) {
208       newval->addAuditor(this, SoNotRec::FIELD);
209       newval->ref();
210 #ifdef COIN_INTERNAL_SOMFPATH
211       SoNode * h = newval->getHead();
212       if (h) {
213         h->addAuditor(this, SoNotRec::FIELD);
214         h->ref();
215       }
216 #endif // COIN_INTERNAL_SOMFPATH
217     }
218 
219     this->setChangedIndex(idx);
220     this->values[idx] = newval;
221 #ifdef COIN_INTERNAL_SOMFPATH
222     this->pathheads[idx] = newval ? newval->getHead() : NULL;
223 #endif // COIN_INTERNAL_SOMFPATH
224   }
225 
226   // Finally, send notification.
227   (void)this->enableNotify(notificstate);
228   if (notificstate) this->valueChanged();
229   this->setChangedIndices();
230 }
231 
232 void
setValue(SoPath * value)233 SoMFPath::setValue(SoPath * value)
234 {
235   this->deleteAllValues();
236   this->set1Value(0, value);
237 }
238 
239 SbBool
operator ==(const SoMFPath & field) const240 SoMFPath::operator==(const SoMFPath & field) const
241 {
242   if (this == &field) return TRUE;
243   if (this->getNum() != field.getNum()) return FALSE;
244 
245   const SoPath ** const lhs = this->getValues(0);
246   const SoPath ** const rhs = field.getValues(0);
247   for (int i = 0; i < this->num; i++) if (lhs[i] != rhs[i]) return FALSE;
248   return TRUE;
249 }
250 
251 /*!
252   \copydoc SoMFFloat::deleteAllValues()
253 */
254 void
deleteAllValues(void)255 SoMFPath::deleteAllValues(void)
256 {
257   // Don't use getNum(), but use this->num directly, since getNum()
258   // might trigger a recursive evaluation call if the field
259   // is connected.
260 
261   if (this->num) this->deleteValues(0);
262 }
263 
264 // Overridden to handle unref() and removeAuditor().
265 void
deleteValues(int start,int numarg)266 SoMFPath::deleteValues(int start, int numarg)
267 {
268   // Note: this function overrides the one in SoMField, so if you do
269   // any changes here, take a look at that method aswell.
270 
271   if (numarg == -1) numarg = this->num - start;
272   for (int i=start; i < start+numarg; i++) {
273     SoPath * n = this->values[i];
274     if (n) {
275       n->removeAuditor(this, SoNotRec::FIELD);
276       n->unref();
277     }
278 #ifdef COIN_INTERNAL_SOMFPATH
279     SoNode * h = this->pathheads[start];
280     this->pathheads.remove(start);
281     if (h) {
282       h->removeAuditor(this, SoNotRec::FIELD);
283       h->unref();
284     }
285 #endif // COIN_INTERNAL_SOMFPATH
286   }
287 
288   inherited::deleteValues(start, numarg);
289 }
290 
291 // Overridden to insert NULL pointers in new array slots.
292 void
insertSpace(int start,int numarg)293 SoMFPath::insertSpace(int start, int numarg)
294 {
295   // Disable temporarily so we don't send notification prematurely
296   // from inherited::insertSpace().
297   SbBool notificstate = this->enableNotify(FALSE);
298   // Important note: the notification state is reset at the end, so
299   // this function should *not* have multiple return-points.
300 
301   inherited::insertSpace(start, numarg);
302   for (int i=start; i < start+numarg; i++) {
303 #ifdef COIN_INTERNAL_SOMFPATH
304     this->pathheads.insert(NULL, start);
305 #endif // COIN_INTERNAL_SOMFPATH
306     this->values[i] = NULL;
307   }
308 
309   // Initialization done, now send notification.
310   (void)this->enableNotify(notificstate);
311   if (notificstate) this->valueChanged();
312 }
313 
314 /*!
315   \copydoc SoMFFloat::copyValue()
316 */
317 void
copyValue(int to,int from)318 SoMFPath::copyValue(int to, int from)
319 {
320   this->values[to] = this->values[from];
321 }
322 
323 //// From the SO_MFIELD_VALUE_SOURCE macro, end. /////////////////////////////
324 
325 
326 // Import a single path.
327 SbBool
read1Value(SoInput * in,int index)328 SoMFPath::read1Value(SoInput * in, int index)
329 {
330   SoSFPath sfpath;
331   SbBool result = sfpath.readValue(in);
332   if (result) this->set1Value(index, sfpath.getValue());
333   return result;
334 }
335 
336 // Export a single path.
337 void
write1Value(SoOutput * out,int idx) const338 SoMFPath::write1Value(SoOutput * out, int idx) const
339 {
340   // NB: This code is common for SoMFNode, SoMFPath and SoMFEngine.
341   // That's why we check for the base type before writing.
342 
343   SoBase * base = this->values[idx];
344   if (base) {
345     if (base->isOfType(SoNode::getClassTypeId())) {
346       coin_assert_cast<SoNode *>(base)->writeInstance(out);
347     }
348     else if (base->isOfType(SoPath::getClassTypeId())) {
349       SoWriteAction wa(out);
350       wa.continueToApply(coin_assert_cast<SoPath *>(base));
351     }
352     else if (base->isOfType(SoEngine::getClassTypeId())) {
353       coin_assert_cast<SoEngine *>(base)->writeInstance(out);
354     }
355   }
356   else {
357     out->write("NULL");
358   }
359 }
360 
361 #endif // DOXYGEN_SKIP_THIS
362 
363 
364 // Overridden from parent to propagate write reference counting to
365 // path.
366 void
countWriteRefs(SoOutput * out) const367 SoMFPath::countWriteRefs(SoOutput * out) const
368 {
369   inherited::countWriteRefs(out);
370 
371   for (int i = 0; i < this->getNum(); i++) {
372     SoBase * base = this->values[i];
373     if (base) {
374       // NB: This code is common for SoMFNode, SoMFPath and SoMFEngine.
375       // That's why we check the base type before writing/counting
376 
377       if (base->isOfType(SoNode::getClassTypeId())) {
378         coin_assert_cast<SoNode *>(base)->writeInstance(out);
379       }
380       else if (base->isOfType(SoEngine::getClassTypeId())) {
381         coin_assert_cast<SoEngine *>(base)->addWriteReference(out);
382       }
383       else if (base->isOfType(SoPath::getClassTypeId())) {
384         SoWriteAction wa(out);
385         wa.continueToApply(coin_assert_cast<SoPath *>(base));
386       }
387     }
388   }
389 }
390 
391 // Override from parent to update our path pointer
392 // references. This is necessary so we do the Right Thing with regard
393 // to the copyconnections flag.
394 //
395 // Note that we have to unplug auditing and the reference counter
396 // addition we made during the copy process.
397 //
398 // For reference for future debugging sessions, copying of this field
399 // goes like this:
400 //
401 //    - copyFrom() is called (typically from SoFieldData::overlay())
402 //    - copyFrom() calls operator=()
403 //    - operator=() calls setValues()
404 //    - we have a local copy (ie not from SoSubField.h) of setValues()
405 //      that sets up auditing and references the array items
406 //
407 // <mortene@sim.no>
408 void
fixCopy(SbBool COIN_UNUSED_ARG (copyconnections))409 SoMFPath::fixCopy(SbBool COIN_UNUSED_ARG(copyconnections))
410 {
411   // Disable temporarily, so we under no circumstances will send more
412   // than one notification about the changes.
413   SbBool notificstate = this->enableNotify(FALSE);
414   // Important note: the notification state is reset at the end, so
415   // this function should *not* have multiple return-points.
416 
417   for (int i=0; i < this->getNum(); i++) {
418     SoPath * n = (*this)[i];
419     if (n) {
420 #if COIN_DEBUG
421       n->assertAlive();
422 #endif // COIN_DEBUG
423       // The set1Value() call below will automatically de-audit and
424       // un-ref the old pointer value node reference we have in the
425       // array, *before* re-inserting a copy.
426 
427 #if defined(COIN_INTERNAL_SOMFNODE) || defined(COIN_INTERNAL_SOMFENGINE)
428       SoFieldContainer * fc = SoFieldContainer::findCopy(n, copyconnections);
429 #if COIN_DEBUG
430       fc->assertAlive();
431 #endif // COIN_DEBUG
432       this->set1Value(i, (SoPath *)fc);
433 #endif // COIN_INTERNAL_SOMFNODE || COIN_INTERNAL_SOMFENGINE
434 
435 #ifdef COIN_INTERNAL_SOMFPATH
436       this->set1Value(i, n->copy());
437 #endif // COIN_INTERNAL_SOMFPATH
438     }
439   }
440 
441   // Finally, send notification.
442   (void)this->enableNotify(notificstate);
443   if (notificstate) this->valueChanged();
444 }
445 
446 // Override from SoField to check path pointer.
447 SbBool
referencesCopy(void) const448 SoMFPath::referencesCopy(void) const
449 {
450   if (inherited::referencesCopy()) return TRUE;
451 
452   for (int i=0; i < this->getNum(); i++) {
453     SoPath * item = (*this)[i];
454     if (item) {
455 #if defined(COIN_INTERNAL_SOMFNODE) || defined(COIN_INTERNAL_SOMFENGINE)
456       if (SoFieldContainer::checkCopy((SoFieldContainer *)item)) return TRUE;
457 #endif // COIN_INTERNAL_SOMFNODE || COIN_INTERNAL_SOMFENGINE
458 #ifdef COIN_INTERNAL_SOMFPATH
459       if (item->getHead() && SoFieldContainer::checkCopy(item->getHead())) return TRUE;
460 #endif // COIN_INTERNAL_SOMFPATH
461     }
462   }
463 
464   return FALSE;
465 }
466 
467 // Kill the type-specific define.
468 #undef COIN_INTERNAL_SOMFPATH
469 //$ END TEMPLATE MFNodeEnginePath
470 
471 
472 // Overridden from superclass to handle changes in paths.
473 void
notify(SoNotList * l)474 SoMFPath::notify(SoNotList * l)
475 {
476   // Detect if any of our paths have gotten a new head :^), and if so
477   // do the necessary audit setup magic.
478   for (int i=0; i < this->getNum(); i++) {
479     SoPath * p = (*this)[i];
480     SoNode * oldhead = this->pathheads[i];
481     if (p && p->getHead() != oldhead) {
482       if (oldhead) {
483         oldhead->removeAuditor(this, SoNotRec::FIELD);
484         oldhead->unref();
485       }
486       SoNode * newhead = this->pathheads[i] = p->getHead();
487       if (newhead) {
488         newhead->addAuditor(this, SoNotRec::FIELD);
489         newhead->ref();
490       }
491     }
492   }
493 
494   inherited::notify(l);
495 }
496 
497 #ifdef COIN_TEST_SUITE
498 
BOOST_AUTO_TEST_CASE(initialized)499 BOOST_AUTO_TEST_CASE(initialized)
500 {
501   SoMFPath field;
502   BOOST_CHECK_MESSAGE(field.getTypeId() != SoType::badType(),
503                       "missing class initialization");
504   BOOST_CHECK_EQUAL(field.getNum(), 0);
505 }
506 
507 #endif // COIN_TEST_SUITE
508