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