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: nearly all of this sourcefile was generated by the
34 // Coin/scripts/templant script from the code in SFNodeEnginePath.tpl.
35 //
36 // Only the notify() function at the bottom of the file deviates from
37 // the templatized code of SoSF[Node|Engine|Path].
38
39 ///////////////////////////////////////////////////////////////////////////
40
41 //$ BEGIN TEMPLATE SFNodeEnginePath(PATH, Path, path)
42
43 /*!
44 \class SoSFPath SoSFPath.h Inventor/fields/SoSFPath.h
45 \brief The SoSFPath class is a container for a single path.
46
47 \ingroup fields
48
49 This field container stores a pointer to a Coin path. It takes care
50 of the necessary functionality for handling copy, import and export
51 operations.
52
53 Note that the path pointer stored in a field instance of this type
54 may be a \c NULL pointer.
55
56 \sa SoPath, SoMFPath
57
58 */
59
60 // Type-specific define to be able to do #ifdef tests on type. (Note:
61 // used to check the header file wrapper define, but that doesn't work
62 // with --enable-compact build.)
63 #define COIN_INTERNAL_SOSFPATH
64
65 #include <Inventor/fields/SoSFPath.h>
66
67 #include "coindefs.h"
68 #include "SbBasicP.h"
69
70 #include <Inventor/SoInput.h>
71 #include <Inventor/SoOutput.h>
72 #include <Inventor/actions/SoWriteAction.h>
73 #include <Inventor/errors/SoReadError.h>
74 #include <Inventor/nodes/SoNode.h>
75 #include <Inventor/engines/SoEngine.h>
76 #include <Inventor/SoOutput.h>
77 #if COIN_DEBUG
78 #include <Inventor/errors/SoDebugError.h>
79 #endif // COIN_DEBUG
80
81 #include "fields/SoSubFieldP.h"
82
83 // Can't use SO_SFIELD_SOURCE() because we need to modify setValue()
84 // to ref and unref the passed path.
85 SO_SFIELD_REQUIRED_SOURCE(SoSFPath);
86
87
88 // Override from parent class.
89 void
initClass(void)90 SoSFPath::initClass(void)
91 {
92 SO_SFIELD_INTERNAL_INIT_CLASS(SoSFPath);
93 }
94
95 // (Declarations hidden in SO_[S|M]FIELD_HEADER macro in header file,
96 // so don't use Doxygen commenting.)
97 #ifndef DOXYGEN_SKIP_THIS
98
99 /* Constructor, sets initial path pointer to a \c NULL pointer. */
SoSFPath(void)100 SoSFPath::SoSFPath(void)
101 {
102 this->value = NULL;
103 #ifdef COIN_INTERNAL_SOSFPATH
104 this->head = NULL;
105 #endif // COIN_INTERNAL_SOSFPATH
106 }
107
108 /* Destructor, dereferences the current path pointer if necessary. */
~SoSFPath(void)109 SoSFPath::~SoSFPath(void)
110 {
111 this->enableNotify(FALSE);
112 this->setValue(NULL);
113 }
114
115 #endif // DOXYGEN_SKIP_THIS
116
117
118 // No need to document readValue() and writeValue() here, as the
119 // necessary information is provided by the documentation of the
120 // parent classes.
121 #ifndef DOXYGEN_SKIP_THIS
122
123 // Store the \a newval path pointer in this field. If \a newval is not
124 // \c NULL, will add 1 to the reference count of the path.
125 void
setValue(SoPath * newval)126 SoSFPath::setValue(SoPath * newval)
127 {
128 // Don't use getValue() to find oldptr, since this might trigger a
129 // recursive evaluation call if the field is connected.
130 SoPath * oldptr = this->value;
131 if (oldptr == newval) return;
132
133 if (oldptr) {
134 #ifdef COIN_INTERNAL_SOSFPATH
135 SoNode * h = oldptr->getHead();
136 // The path should be audited by us at all times. So don't use
137 // SoSFPath to wrap SoTempPath or SoLightPath, for instance.
138 assert(h==this->head && "Path head changed without notification!");
139 if (h) {
140 h->removeAuditor(this, SoNotRec::FIELD);
141 h->unref();
142 }
143 #endif // COIN_INTERNAL_SOSFPATH
144 oldptr->removeAuditor(this, SoNotRec::FIELD);
145 oldptr->unref();
146 }
147
148 if (newval) {
149 newval->addAuditor(this, SoNotRec::FIELD);
150 newval->ref();
151 #ifdef COIN_INTERNAL_SOSFPATH
152 this->head = newval->getHead();
153 if (this->head) {
154 this->head->addAuditor(this, SoNotRec::FIELD);
155 this->head->ref();
156 }
157 #endif // COIN_INTERNAL_SOSFPATH
158 }
159
160 this->value = newval;
161 this->valueChanged();
162 }
163
164 // Compares to see if the \a field points to the same path as this
165 // field does, and returns \c TRUE if this is the case.
166 //
167 // Be aware that this method does \e not check for path/subgraph
168 // equality if the pointers are not the same, so \c FALSE is returned
169 // even though the contents of the path/subgraph are equal.
170 SbBool
operator ==(const SoSFPath & field) const171 SoSFPath::operator==(const SoSFPath & field) const
172 {
173 return (this->getValue() == field.getValue());
174 }
175
176 // Import path.
177 SbBool
readValue(SoInput * in)178 SoSFPath::readValue(SoInput * in)
179 {
180 SoBase * baseptr;
181
182 //Handle when the path is set to NULL
183 SbName keyword;
184 if (in)
185 if (!in->read(keyword)) return FALSE;
186
187 if(keyword=="NULL") {
188 this->setValue(NULL);
189 return TRUE;
190 }
191 else
192 in->putBack(keyword.getString());
193
194 if (!SoBase::read(in, baseptr, SoPath::getClassTypeId())) return FALSE;
195
196 if (in->eof()) {
197 SoReadError::post(in, "Premature end of file");
198 return FALSE;
199 }
200 if (!baseptr) {
201 SoReadError::post(in, "Unable to read value for SoSFPath");
202 return FALSE;
203 }
204
205 this->setValue(coin_assert_cast<SoPath *>(baseptr));
206 return TRUE;
207 }
208
209 // Export path.
210 void
writeValue(SoOutput * out) const211 SoSFPath::writeValue(SoOutput * out) const
212 {
213 // NB: This code is common for SoSFNode, SoSFPath and SoSFEngine.
214 // That's why we check the base type before writing.
215 SoBase * base = this->getValue();
216 if (base) {
217 if (base->isOfType(SoNode::getClassTypeId())) {
218 coin_assert_cast<SoNode *>(base)->writeInstance(out);
219 }
220 else if (base->isOfType(SoPath::getClassTypeId())) {
221 SoWriteAction wa(out);
222 wa.continueToApply(coin_assert_cast<SoPath *>(base));
223 }
224 else if (base->isOfType(SoEngine::getClassTypeId())) {
225 coin_assert_cast<SoEngine *>(base)->writeInstance(out);
226 }
227 else {
228 assert(0 && "strange internal error");
229 }
230 }
231 else {
232 // This actually works for both ASCII and binary formats.
233 out->write("NULL");
234 }
235 }
236
237 #endif // DOXYGEN_SKIP_THIS
238
239
240 // Overridden from parent to propagate write reference counting to
241 // path.
242 void
countWriteRefs(SoOutput * out) const243 SoSFPath::countWriteRefs(SoOutput * out) const
244 {
245 inherited::countWriteRefs(out);
246
247 SoBase * base = this->getValue();
248 if (base == NULL) return;
249
250 // NB: This code is common for SoSFNode, SoSFPath and SoSFEngine.
251 // That's why we check the base type before writing/counting
252
253 if (base->isOfType(SoNode::getClassTypeId())) {
254 coin_assert_cast<SoNode *>(base)->writeInstance(out);
255 }
256 else if (base->isOfType(SoEngine::getClassTypeId())) {
257 coin_assert_cast<SoEngine *>(base)->addWriteReference(out);
258 }
259 else if (base->isOfType(SoPath::getClassTypeId())) {
260 SoWriteAction wa(out);
261 wa.continueToApply(coin_assert_cast<SoPath *>(base));
262 }
263 }
264
265 // Override from parent to update our path pointer
266 // reference. This is necessary so we do the Right Thing with regard
267 // to the copyconnections flag.
268 //
269 // Note that we have to unplug auditing and the reference counter
270 // addition we made during the copy process.
271 //
272 // For reference for future debugging sessions, copying of this field
273 // goes like this:
274 //
275 // - copyFrom() is called (typically from SoFieldData::overlay())
276 // - copyFrom() calls operator=()
277 // - operator=() calls setValue()
278 // - we have a local copy (ie not from SoSubField.h) of setValue()
279 // that sets up auditing and references the item
280 //
281 // <mortene@sim.no>
282 void
fixCopy(SbBool COIN_UNUSED_ARG (copyconnections))283 SoSFPath::fixCopy(SbBool COIN_UNUSED_ARG(copyconnections))
284 {
285 SoPath * n = this->getValue();
286 if (!n) return;
287
288 #if COIN_DEBUG
289 n->assertAlive();
290 #endif // COIN_DEBUG
291
292 // The setValue() call below will automatically de-audit and un-ref
293 // the old pointer-value reference we have, *before* re-inserting a
294 // copy.
295
296 #if defined(COIN_INTERNAL_SOSFNODE) || defined(COIN_INTERNAL_SOSFENGINE)
297 SoFieldContainer * fc = SoFieldContainer::findCopy(n, copyconnections);
298 #if COIN_DEBUG
299 fc->assertAlive();
300 #endif // COIN_DEBUG
301 this->setValue((SoPath *)fc);
302 #endif // COIN_INTERNAL_SOSFNODE || COIN_INTERNAL_SOSFENGINE
303
304 #ifdef COIN_INTERNAL_SOSFPATH
305 this->setValue(n->copy());
306 #endif // COIN_INTERNAL_SOSFPATH
307 }
308
309 // Override from SoField to check path pointer.
310 SbBool
referencesCopy(void) const311 SoSFPath::referencesCopy(void) const
312 {
313 if (inherited::referencesCopy()) return TRUE;
314
315 SoBase * n = this->getValue();
316 if (!n) return FALSE;
317
318 if (n->isOfType(SoNode::getClassTypeId()) ||
319 n->isOfType(SoEngine::getClassTypeId())) {
320 if (SoFieldContainer::checkCopy(coin_assert_cast<SoFieldContainer *>(n))) return TRUE;
321 }
322 else if (n->isOfType(SoPath::getClassTypeId())) {
323 SoPath * p = coin_assert_cast<SoPath *>(n);
324 if (p->getHead() == NULL) return FALSE;
325 if (SoFieldContainer::checkCopy(p->getHead())) return TRUE;
326 }
327 else {
328 assert(0 && "strange internal error");
329 }
330
331 return FALSE;
332 }
333
334 // Kill the type-specific define.
335 #undef COIN_INTERNAL_SOSFPATH
336 //$ END TEMPLATE SFNodeEnginePath
337
338
339 void
notify(SoNotList * l)340 SoSFPath::notify(SoNotList * l)
341 {
342 // Detect if our path has gotten a new head :^), and if so do the
343 // necessary audit setup magic.
344 if (this->getValue() && this->getValue()->getHead() != this->head) {
345 if (this->head) {
346 this->head->removeAuditor(this, SoNotRec::FIELD);
347 this->head->unref();
348 }
349 this->head = this->getValue()->getHead();
350 if (this->head) {
351 this->head->addAuditor(this, SoNotRec::FIELD);
352 this->head->ref();
353 }
354 }
355
356 inherited::notify(l);
357 }
358
359
360 #ifdef COIN_TEST_SUITE
361
BOOST_AUTO_TEST_CASE(initialized)362 BOOST_AUTO_TEST_CASE(initialized)
363 {
364 SoSFPath field;
365 BOOST_CHECK_MESSAGE(SoSFPath::getClassTypeId() != SoType::badType(),
366 "SoSFPath class not initialized");
367 BOOST_CHECK_MESSAGE(field.getTypeId() != SoType::badType(),
368 "missing class initialization");
369 }
370
371 #endif // COIN_TEST_SUITE
372