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 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36 
37 #ifdef HAVE_NODEKITS
38 
39 /*!
40   \class SoNodekitCatalog SoNodekitCatalog.h Inventor/nodekits/SoNodekitCatalog.h
41   \brief The SoNodekitCatalog class is a container for nodekit layouts.
42 
43   \ingroup nodekits
44 
45   Nodekits store all their hierarchical layout information and part
46   information in instances of this class.
47 
48   \sa SoNodeKit, SoBaseKit
49 */
50 
51 #include <Inventor/nodekits/SoBaseKit.h>
52 
53 #include <cassert>
54 #include <cstdio> // fprintf()
55 
56 #include <Inventor/lists/SoTypeList.h>
57 #if COIN_DEBUG
58 #include <Inventor/errors/SoDebugError.h>
59 #endif // COIN_DEBUG
60 
61 #include "threads/threadsutilp.h"
62 
63 // Private container class.
64 class CatalogItem {
65 public:
CatalogItem(void)66   CatalogItem(void) { }
~CatalogItem()67   ~CatalogItem() { }
68 
69   SbName name, parentname, siblingname;
70   SoType type, defaulttype, containertype;
71   SbBool isdefaultnull, islist, ispublic;
72   SoTypeList itemtypeslist;
73 };
74 
75 /*!
76   Initialization of static variables.
77 */
78 void
initClass(void)79 SoNodekitCatalog::initClass(void)
80 {
81 }
82 
83 /*!
84   Constructor.
85 */
SoNodekitCatalog(void)86 SoNodekitCatalog::SoNodekitCatalog(void)
87 {
88 }
89 
90 /*!
91   Destructor.
92 */
~SoNodekitCatalog()93 SoNodekitCatalog::~SoNodekitCatalog()
94 {
95   int i;
96   for (i=0; i < this->items.getLength(); i++)
97     delete this->items[i];
98   for (i=0; i < this->delayeditems.getLength(); i++)
99     delete this->delayeditems[i];
100 }
101 
102 /*!
103   Returns total number of entries in the catalog.
104 */
105 int
getNumEntries(void) const106 SoNodekitCatalog::getNumEntries(void) const
107 {
108   assert(this->delayeditems.getLength() == 0);
109   return this->items.getLength();
110 }
111 
112 /*!
113   Returns part number in catalog with \a name. If no part exists with
114   \a name, returns \c SO_CATALOG_NAME_NOT_FOUND.
115 */
116 int
getPartNumber(const SbName & name) const117 SoNodekitCatalog::getPartNumber(const SbName & name) const
118 {
119   assert(this->delayeditems.getLength() == 0);
120   return this->getPartNumber(this->items, name);
121 }
122 
123 /*!
124   Given the \a part number, return name of that part.
125 */
126 const SbName &
getName(int part) const127 SoNodekitCatalog::getName(int part) const
128 {
129   assert( this->delayeditems.getLength() == 0 );
130   assert( part >= 0 && part < this->getNumEntries() &&
131           "invalid part" );
132 
133   return this->items[part]->name;
134 }
135 
136 /*!
137   Given the \a part number, return type.
138 */
139 SoType
getType(int part) const140 SoNodekitCatalog::getType(int part) const
141 {
142   assert(this->delayeditems.getLength() == 0);
143   assert( part >= 0 && part < this->getNumEntries() &&
144           "invalid part" );
145   return this->items[part]->type;
146 }
147 
148 /*!
149   Given the part \a name, return type.
150 */
151 SoType
getType(const SbName & name) const152 SoNodekitCatalog::getType(const SbName & name) const
153 {
154   assert(this->delayeditems.getLength() == 0);
155   return this->getType( this->getPartNumber( name ) );
156 }
157 
158 /*!
159   Given \a part number, return default type of part.
160 */
161 SoType
getDefaultType(int part) const162 SoNodekitCatalog::getDefaultType(int part) const
163 {
164   assert(this->delayeditems.getLength() == 0);
165   assert( part >= 0 && part < this->getNumEntries() &&
166           "invalid part" );
167 
168   return this->items[part]->defaulttype;
169 }
170 
171 /*!
172   Given part \a name, return default type of part.
173 */
174 SoType
getDefaultType(const SbName & name) const175 SoNodekitCatalog::getDefaultType(const SbName & name) const
176 {
177   assert( this->delayeditems.getLength() == 0 );
178   return this->getDefaultType( this->getPartNumber( name ) );
179 }
180 
181 /*!
182   Returns \c TRUE if the \a part is empty by default, otherwise \c FALSE.
183 */
184 SbBool
isNullByDefault(int part) const185 SoNodekitCatalog::isNullByDefault(int part) const
186 {
187   assert( this->delayeditems.getLength() == 0 );
188   assert( part != SO_CATALOG_NAME_NOT_FOUND &&
189           "invalid part");
190 
191   return this->items[part]->isdefaultnull;
192 }
193 
194 // Seems like there's a Doxygen bug (version 1.2.1, at least) as it
195 // reports isNullByDefault(const SbName & name) as undocumented
196 // without the explicit \fn.
197 
198 /*!
199   \fn SbBool SoNodekitCatalog::isNullByDefault(const SbName & name) const
200 
201   Returns \c TRUE if part \a name is empty by default, otherwise \c
202   FALSE.
203 */
204 SbBool
isNullByDefault(const SbName & name) const205 SoNodekitCatalog::isNullByDefault(const SbName & name) const
206 {
207   assert(this->delayeditems.getLength() == 0);
208   return this->isNullByDefault( this->getPartNumber(name) );
209 }
210 
211 /*!
212   Returns \c TRUE if the \a part is \e not a parent for any
213   other parts in the nodekit catalog.
214 */
215 SbBool
isLeaf(int part) const216 SoNodekitCatalog::isLeaf(int part) const
217 {
218   assert( this->delayeditems.getLength() == 0);
219   assert( part >= 0 && part < this->getNumEntries() &&
220           "invalid part" );
221 
222   for (int i=0; i < this->items.getLength(); i++) {
223     if ((i != part) && (this->items[part]->name == this->items[i]->parentname))
224       return FALSE;
225   }
226   return TRUE;
227 }
228 
229 /*!
230   Returns \c TRUE if the part \a name is \e not a parent for any
231   other parts in the nodekit catalog.
232 */
233 SbBool
isLeaf(const SbName & name) const234 SoNodekitCatalog::isLeaf(const SbName & name) const
235 {
236   assert(this->delayeditems.getLength() == 0);
237   return this->isLeaf( this->getPartNumber( name ));
238 }
239 
240 /*!
241   Returns name of parent of \a part. If \a part doesn't have a parent,
242   the empty string is returned.
243 */
244 const SbName &
getParentName(int part) const245 SoNodekitCatalog::getParentName(int part) const
246 {
247   assert( this->delayeditems.getLength() == 0);
248   assert( part >= 0 && part < this->getNumEntries() &&
249           "invalid part" );
250   return this->items[part]->parentname;
251 }
252 
253 /*!
254   Returns name of parent of the part. If \a name doesn't have a parent,
255   the empty string is returned.
256 */
257 const SbName &
getParentName(const SbName & name) const258 SoNodekitCatalog::getParentName(const SbName & name) const
259 {
260   assert(this->delayeditems.getLength() == 0);
261   return this->getParentName( this->getPartNumber(name) );
262 }
263 
264 /*!
265   Returns part number of given part's parent. If \a part doesn't have a parent,
266   SO_CATALOG_NAME_NOT_FOUND is returned.
267 */
268 int
getParentPartNumber(int part) const269 SoNodekitCatalog::getParentPartNumber(int part) const
270 {
271   assert( this->delayeditems.getLength() == 0 );
272   assert( part >= 0 && part < this->getNumEntries() &&
273           "invalid part" );
274 
275   return this->getPartNumber(this->items[part]->parentname);
276 }
277 
278 /*!
279   Returns part number of given part's parent. If \a name doesn't have a parent,
280   SO_CATALOG_NAME_NOT_FOUND is returned.
281 */
282 int
getParentPartNumber(const SbName & name) const283 SoNodekitCatalog::getParentPartNumber(const SbName & name) const
284 {
285   assert(this->delayeditems.getLength() == 0);
286   return this->getParentPartNumber( this->getPartNumber(name) );
287 }
288 
289 /*!
290   Returns name of right sibling of \a part. Returns the empty string if
291   \a part doesn't have a right sibling.
292 */
293 const SbName &
getRightSiblingName(int part) const294 SoNodekitCatalog::getRightSiblingName(int part) const
295 {
296   assert(this->delayeditems.getLength() == 0);
297   assert( part >= 0 && part < this->getNumEntries() &&
298           "invalid part" );
299 
300   return this->items[part]->siblingname;
301 }
302 
303 
304 /*!
305   Returns name of sibling of the part. Returns the empty string if
306   \a name doesn't have a right sibling.
307 */
308 const SbName &
getRightSiblingName(const SbName & name) const309 SoNodekitCatalog::getRightSiblingName(const SbName & name) const
310 {
311   assert(this->delayeditems.getLength() == 0);
312   return this->getRightSiblingName( this->getPartNumber(name) );
313 }
314 
315 /*!
316   Returns part number of given part's sibling. Returns
317   SO_CATALOG_NAME_NOT_FOUND if \a part doesn't have a right sibling.
318 */
319 int
getRightSiblingPartNumber(int part) const320 SoNodekitCatalog::getRightSiblingPartNumber(int part) const
321 {
322   assert(this->delayeditems.getLength() == 0);
323   assert( part >= 0 && part < this->getNumEntries() &&
324           "invalid part" );
325 
326   return this->getPartNumber(this->items[part]->siblingname);
327 }
328 
329 /*!
330   Returns part number of given part's right sibling. Returns
331   SO_CATALOG_NAME_NOT_FOUND if part doesn't have a right sibling.
332 */
333 int
getRightSiblingPartNumber(const SbName & name) const334 SoNodekitCatalog::getRightSiblingPartNumber(const SbName & name) const
335 {
336   assert(this->delayeditems.getLength() == 0);
337 
338   return this->getRightSiblingPartNumber( this->getPartNumber(name) );
339 }
340 
341 /*!
342   Returns \c TRUE if the given \a part is a list container.
343 */
344 SbBool
isList(int part) const345 SoNodekitCatalog::isList(int part) const
346 {
347   assert(this->delayeditems.getLength() == 0);
348   assert( part >= 0 && part < this->getNumEntries() &&
349           "invalid part" );
350 
351   return this->items[part]->islist;
352 }
353 
354 /*!
355   Returns \c TRUE if the given part is a list container.
356 */
357 SbBool
isList(const SbName & name) const358 SoNodekitCatalog::isList(const SbName & name) const
359 {
360   assert(this->delayeditems.getLength() == 0);
361   return this->isList( this->getPartNumber(name) );
362 }
363 
364 /*!
365   Returns type of list container (SoGroup, SoSeparator, SoSwitch, etc)
366   which \a part is.
367 */
368 SoType
getListContainerType(int part) const369 SoNodekitCatalog::getListContainerType(int part) const
370 {
371   assert( this->delayeditems.getLength() == 0 );
372   assert( part >= 0 && part < this->getNumEntries() &&
373           "invalid part" );
374   assert( this->items[part]->islist &&
375           "not a list container" );
376 
377   return this->items[part]->containertype;
378 }
379 
380 /*!
381   Returns type of list container (SoGroup, SoSeparator, SoSwitch, etc)
382   which the named part is.
383 */
384 SoType
getListContainerType(const SbName & name) const385 SoNodekitCatalog::getListContainerType(const SbName & name) const
386 {
387   assert(this->delayeditems.getLength() == 0);
388   return this->getListContainerType( this->getPartNumber(name) );
389 }
390 
391 /*!
392   Returns list of node types which are allowed to be children of the
393   list container \a part.
394 */
395 const SoTypeList &
getListItemTypes(int part) const396 SoNodekitCatalog::getListItemTypes(int part) const
397 {
398   assert( this->delayeditems.getLength() == 0);
399   assert( (part >= 0 && part < this->getNumEntries()) &&
400           "invalid part");
401   assert( (this->items[part]->islist) &&
402           "part is not a list container" );
403 
404   return this->items[part]->itemtypeslist;
405 }
406 
407 /*!
408   Returns list of node types which are allowed to be children of the
409   named list container part.
410 */
411 const SoTypeList &
getListItemTypes(const SbName & name) const412 SoNodekitCatalog::getListItemTypes(const SbName & name) const
413 {
414   assert(this->delayeditems.getLength() == 0);
415   return this->getListItemTypes( this->getPartNumber(name) );
416 }
417 
418 /*!
419   Returns \c TRUE if \a part is visible and publicly available for
420   queries and modifications, \c FALSE if \a part is hidden.
421 */
422 SbBool
isPublic(int part) const423 SoNodekitCatalog::isPublic(int part) const
424 {
425   assert( this->delayeditems.getLength() == 0 );
426   assert( (part >= 0 && part < this->getNumEntries()) && "invalid part" );
427 
428   return this->items[part]->ispublic;
429 }
430 
431 /*!
432   Returns \c TRUE if the part is visible and publicly available for
433   queries and modifications, \c FALSE if it is hidden.
434 */
435 SbBool
isPublic(const SbName & name) const436 SoNodekitCatalog::isPublic(const SbName & name) const
437 {
438   assert(this->delayeditems.getLength() == 0);
439   return this->isPublic( this->getPartNumber(name) );
440 }
441 
442 /*!
443   Return a clone of this catalog. \a type will be used to set the type
444   and defaulttype values of the toplevel \c this entry.
445 */
446 SoNodekitCatalog *
clone(SoType type) const447 SoNodekitCatalog::clone(SoType type) const
448 {
449   assert(this->delayeditems.getLength() == 0);
450 
451   SoNodekitCatalog * newcat = new SoNodekitCatalog;
452   for (int i=0; i < this->items.getLength(); i++) {
453     CatalogItem * olditem = this->items[i];
454     CatalogItem * newitem = new CatalogItem(*olditem);
455     if (i == 0) {
456       newitem->type = type;
457       newitem->defaulttype = type;
458     }
459     // This is the only element in CatalogItem which can't be bitwise
460     // copied.
461     newitem->itemtypeslist = olditem->itemtypeslist;
462     newcat->items.append(newitem);
463   }
464   return newcat;
465 }
466 
SoNodekitCatalogPropagateDefaultInit(SoNodekitCatalog * pthis)467 static void SoNodekitCatalogPropagateDefaultInit( SoNodekitCatalog * pthis )
468 {
469 
470 #if COIN_DEBUG && 0
471   SoDebugError::postInfo("SoNodekitCatalogPropagateDefaultInit",
472                          "sanitizing catalog" );
473 #endif
474 
475   for( int i = pthis->getNumEntries()-1; i > 0; --i ){
476     if( !pthis->isNullByDefault( i ) ){
477       SbName parent = pthis->getParentName( i );
478       if( pthis->isNullByDefault( parent ) )
479         pthis->setNullByDefault( parent, FALSE );
480     }
481   }
482 }
483 
484 
485 /*!
486   Add a new entry to the catalog. Returns \c TRUE if add was ok.
487 */
488 SbBool
addEntry(const SbName & name,SoType type,SoType defaulttype,SbBool isdefaultnull,const SbName & parentname,const SbName & rightsiblingname,SbBool islist,SoType listcontainertype,SoType listitemtype,SbBool ispublic)489 SoNodekitCatalog::addEntry(const SbName & name, SoType type,
490                            SoType defaulttype, SbBool isdefaultnull,
491                            const SbName & parentname,
492                            const SbName & rightsiblingname,
493                            SbBool islist, SoType listcontainertype,
494                            SoType listitemtype, SbBool ispublic)
495 {
496   // The elements of a nodekit catalog is conceptually ordered like a
497   // tree, but implementation-wise we stuff them inside a list. This
498   // will make it speedier to access the elements through preset part
499   // number indices.
500   //
501   // The list is in the same order which we would get by continually
502   // appending elements through a prefix traversal of the tree.
503 
504   // Note: this is a fix to make it possible to compile the
505   // SO_KIT_ADD_CATALOG_ENTRY() etc macros under MS VisualC++ with ""
506   // entries where you can specify blank , , entries under gcc.
507   SbName parent = parentname;
508   SbName rightsibling = rightsiblingname;
509   if (parent[0] == '\"' && parent[1] == '\"') parent = "";
510   if (rightsibling[0] == '\"' && rightsibling[1] == '\"') rightsibling = "";
511 
512 
513 #if COIN_DEBUG && 0
514   SoDebugError::postInfo("SoNodekitCatalog::addEntry",
515                          "new entry: ``%s''", name.getString());
516 #endif
517 
518   CC_GLOBAL_LOCK;
519   if (!this->hasEntry(name)) {
520     assert((name != "") && "Empty name not allowed");
521     assert((this->getPartNumber( this->items, name ) == SO_CATALOG_NAME_NOT_FOUND ) && "partname already in use" );
522     assert(this->getPartNumber( this->delayeditems, name ) == SO_CATALOG_NAME_NOT_FOUND && "partname already in use" );
523     assert( (parent!="" || this->getNumEntries() == 0) && "need a parent name" );
524     assert( (type != SoType::badType()) && "bad type" );
525     assert( (defaulttype != SoType::badType()) && "bad default type" );
526 
527     // NOTE: !(A ^ B) <=> !A v !B
528     assert( (!islist || (listcontainertype != SoType::badType())) &&
529             "bad list container type");
530     assert( (!islist || (listitemtype != SoType::badType())) &&
531             "bad list item type" );
532 
533     CatalogItem * newitem = new CatalogItem;
534     newitem->name = name;
535     newitem->type = type;
536     newitem->defaulttype = defaulttype;
537     newitem->isdefaultnull = isdefaultnull;
538     newitem->parentname = parent;
539     newitem->siblingname = rightsibling;
540     newitem->islist = islist;
541     newitem->containertype = listcontainertype;
542     newitem->itemtypeslist.append(listitemtype);
543     newitem->ispublic = ispublic;
544 
545     SbBool delay = FALSE;
546     if (rightsibling != "" &&
547         this->getPartNumber(this->items, rightsibling) == SO_CATALOG_NAME_NOT_FOUND) {
548       delay = TRUE;
549     }
550     else if (parent != "" &&
551              this->getPartNumber(this->items, parent) == SO_CATALOG_NAME_NOT_FOUND) {
552       delay = TRUE;
553     }
554 
555     if (delay)
556       this->delayeditems.append(newitem);
557     else
558       this->reallyAddEntry(newitem);
559 
560     // Move elements from list of delayed inserts to "real" catalog
561     // list, if possible.
562     for (int i = 0; i < this->delayeditems.getLength(); i++) {
563       const SbName & p = this->delayeditems[i]->parentname;
564       const SbName & r = this->delayeditems[i]->siblingname;
565 
566       if (this->getPartNumber(this->items, p) != SO_CATALOG_NAME_NOT_FOUND &&
567           ((r == "") ||
568            (this->getPartNumber(this->items, r) != SO_CATALOG_NAME_NOT_FOUND))){
569 
570         this->reallyAddEntry(this->delayeditems[i]);
571         this->delayeditems.remove(i);
572         i = -1; // restart scan
573 
574 #if COIN_DEBUG && 0
575         SoDebugError::postInfo("SoNodekitCatalog::addEntry",
576                                "fixed delayed item, %d item%s left",
577                                this->delayeditems.getLength(),
578                                this->delayeditems.getLength()==1 ? "" : "s");
579 #endif
580       }
581     }
582 
583     if( this->delayeditems.getLength() == 0 )
584       SoNodekitCatalogPropagateDefaultInit( this );
585   }
586   CC_GLOBAL_UNLOCK;
587   return TRUE;
588 }
589 
590 // Add the item at the correct position in the entry list, where the
591 // arguemtn "newitem" is guaranteed to have both parent and rigt
592 // sibling (if any) present in the catalog.
593 SbBool
reallyAddEntry(CatalogItem * newitem)594 SoNodekitCatalog::reallyAddEntry(CatalogItem * newitem)
595 {
596   const int n = this->items.getLength();
597 
598   if (n == 0) {
599     this->items.append(newitem);
600     return TRUE;
601   }
602 
603   int position = -1;
604   for (int i = 0; i < n; i++) {
605     if ((this->items[i]->parentname == newitem->parentname) &&
606         (this->items[i]->siblingname == newitem->siblingname)) {
607       // this might happen when extending the catalog of another nodekit
608       this->items[i]->siblingname = newitem->name;
609       position = i+1;
610       break;
611     }
612   }
613   if (position < 0) {
614     position = 0;
615     while (position < n &&
616            (this->items[position]->name != newitem->siblingname ||
617             this->items[position]->parentname != newitem->parentname)) position++;
618     if (position == n) {
619       // parent and sibling not found, insert item after the parent
620       position = this->getPartNumber(this->items, newitem->parentname) + 1;
621     }
622   }
623   if (position == this->items.getLength())
624     this->items.append(newitem);
625   else
626     this->items.insert(newitem, position);
627   return TRUE;
628 }
629 
630 /*!
631   Add another allowable type for the given \a part. \a part must of course
632   be a list container item.
633 */
634 void
addListItemType(int part,SoType type)635 SoNodekitCatalog::addListItemType(int part, SoType type)
636 {
637   assert(this->delayeditems.getLength() == 0);
638   this->addListItemType(this->items, part, type);
639 }
640 
641 /*!
642   Add another allowable type for the \a name part. The part must of course
643   be a list container.
644 */
645 void
addListItemType(const SbName & name,SoType type)646 SoNodekitCatalog::addListItemType(const SbName & name, SoType type)
647 {
648   CC_GLOBAL_LOCK;
649   if (!this->hasListItemType(name, type)) {
650     // FIXME: If a part name is invalid, this procedure bails out
651     // elsewhere on an assert. The check and debug comment should be
652     // superflous? If it isn't, it should be possible to find a way to
653     // write this that expresses the code intentions better.
654     // 20021029 rolvs
655 
656     if (!this->addListItemType(this->items, name, type) &&
657         !this->addListItemType(this->delayeditems, name, type)) {
658 #if COIN_DEBUG
659       SoDebugError::post("SoNodekitCatalog::addListItemType",
660                          "invalid part name, \"%s\"", name.getString());
661 #endif
662     }
663   }
664   CC_GLOBAL_UNLOCK;
665 }
666 
667 /*!
668   Set the type and default type of a part to be subtypes of the old
669   types. Useful for "narrowing" the specification of a nodekit which
670   inherits the catalog of a more generic nodekit superclass.
671 */
672 void
narrowTypes(const SbName & name,SoType newtype,SoType newdefaulttype)673 SoNodekitCatalog::narrowTypes(const SbName & name,
674                               SoType newtype, SoType newdefaulttype)
675 {
676   CC_GLOBAL_LOCK;
677 
678   assert(this->delayeditems.getLength() == 0);
679 
680   int part = this->getPartNumber(name);
681 
682   assert( part != SO_CATALOG_NAME_NOT_FOUND &&
683           "invalid part name" );
684   assert( newtype.isDerivedFrom( this->items[part]->type ) &&
685           "new type should be derived of old type" );
686   assert( newdefaulttype.isDerivedFrom( this->items[part]->type ) &&
687           "new type should be derived of old type" );
688 
689   this->items[part]->type = newtype;
690   this->items[part]->defaulttype = newdefaulttype;
691 
692   CC_GLOBAL_UNLOCK;
693 }
694 
695 /*!
696   Change whether or not the part with the given \a name is created by
697   default.
698 */
699 void
setNullByDefault(const SbName & name,SbBool nullbydefault)700 SoNodekitCatalog::setNullByDefault(const SbName & name, SbBool nullbydefault)
701 {
702   assert(this->delayeditems.getLength() == 0);
703 
704   int part = this->getPartNumber(name);
705   assert( part != SO_CATALOG_NAME_NOT_FOUND &&
706           "invalid part name" );
707 
708   this->items[part]->isdefaultnull = nullbydefault;
709 }
710 
711 /*!
712   Recursively search \a part number in catalog for the \a name part.
713 
714   The \a checked SoTypeList is just used as a placeholder to remember which
715   nodekit class catalogs have already been scanned (or are being scanned)
716   during the recursion. You should normally just pass in an empty list.
717 */
718 SbBool
recursiveSearch(int part,const SbName & name,SoTypeList * checked) const719 SoNodekitCatalog::recursiveSearch(int part, const SbName & name,
720                                   SoTypeList * checked) const
721 {
722   assert( this->delayeditems.getLength() == 0 );
723   assert( part >= 0 && part < this->getNumEntries() &&
724           "part index out of bounds");
725 
726   if ((part == 0) && (checked->find(this->getType(0)) == -1))
727     checked->append(this->getType(0));
728 
729   int start = (part == 0) ? 1 : part;
730   int end = (part == 0) ? this->getNumEntries()-1 : part;
731 
732   for (int i = start; i <= end; i++) {
733     if (name == this->getName(i)) return TRUE;
734 
735     SoType parttype = this->getType(i);
736     if (parttype.isDerivedFrom(SoBaseKit::getClassTypeId())) {
737       if (checked->find(parttype) == -1) {
738         checked->append(parttype);
739         SoBaseKit * kit = (SoBaseKit *)parttype.createInstance();
740         kit->ref();
741         const SoNodekitCatalog * cat = kit->getNodekitCatalog();
742         SbBool result = cat->recursiveSearch(0, name, checked);
743         kit->unref();
744         if (result) return TRUE;
745       }
746     }
747   }
748 
749   return FALSE;
750 }
751 
752 /*!
753   Lists all catalog parts, which is useful for debugging.
754 */
755 void
printCheck(void) const756 SoNodekitCatalog::printCheck(void) const
757 {
758   int nritems = this->getNumEntries();
759   fprintf(stdout, "catalog printout: number of entries = %d\n", nritems);
760   for (int i=0; i < nritems; i++) {
761     CatalogItem * item = this->items[i];
762 
763     fprintf(stdout,
764             "#%d\n"
765             "    name = %s, type = %s, defaultType = %s\n"
766             "    nullByDefault = %d\n"
767             "    parentName = %s\n"
768             "    sibling = %s, listPart = %d\n",
769             i, item->name.getString(),
770             item->type == SoType::badType() ? "*bad*" : item->type.getName().getString(),
771             item->defaulttype == SoType::badType() ? "*bad*" : item->defaulttype.getName().getString(),
772             item->isdefaultnull, item->parentname.getString(),
773             item->siblingname.getString(), item->islist);
774 
775     if (item->islist) {
776       fprintf(stdout, "listItemTypes =");
777       for (int j=0; j < item->itemtypeslist.getLength(); j++) {
778         fprintf(stdout, " %s", item->itemtypeslist[j].getName().getString());
779       }
780       fprintf(stdout, "\n");
781     }
782 
783     fprintf(stdout, "    publicPart = %d\n", item->ispublic);
784   }
785 }
786 
787 // Overloaded to work with both delayed and "real" list of entries.
788 int
getPartNumber(const SbList<class CatalogItem * > & l,const SbName & name) const789 SoNodekitCatalog::getPartNumber(const SbList<class CatalogItem *> & l,
790                                 const SbName & name) const
791 {
792   int nritems = l.getLength();
793   for (int i= 0; i < nritems; i++) {
794     if (name == l[i]->name) return i;
795   }
796   return SO_CATALOG_NAME_NOT_FOUND;
797 }
798 
799 // Overloaded to work with both delayed and "real" list of entries.
800 void
addListItemType(const SbList<class CatalogItem * > & l,int part,SoType type)801 SoNodekitCatalog::addListItemType(const SbList<class CatalogItem *> & l,
802                                   int part, SoType type)
803 {
804   assert( part >= 0 && part < l.getLength() &&
805           "invalid part number" );
806   assert( type != SoType::badType() &&
807           "dont add SoType::badType(), you stupid!");
808   assert( l[part]->islist &&
809           "type must be a list-item type" );
810   assert( l[part]->itemtypeslist.find( type ) == -1 &&
811           "trying to add item that allready exists" );
812 
813   l[part]->itemtypeslist.append(type);
814 }
815 
816 // Overloaded to work with both delayed and "real" list of entries.
817 SbBool
addListItemType(const SbList<class CatalogItem * > & l,const SbName & name,SoType type)818 SoNodekitCatalog::addListItemType(const SbList<class CatalogItem *> & l,
819                                   const SbName & name, SoType type)
820 {
821   int part = this->getPartNumber(l, name);
822   if (part == SO_CATALOG_NAME_NOT_FOUND) return FALSE;
823   this->addListItemType(l, part, type);
824   return TRUE;
825 }
826 
827 /*!
828   \internal
829   \since Coin 2.3
830 */
831 SbBool
hasEntry(const SbName & name) const832 SoNodekitCatalog::hasEntry(const SbName & name) const
833 {
834   if (this->getPartNumber(this->items, name) != SO_CATALOG_NAME_NOT_FOUND) return TRUE;
835   if (this->getPartNumber(this->delayeditems, name) != SO_CATALOG_NAME_NOT_FOUND) return TRUE;
836   return FALSE;
837 }
838 
839 /*!
840   \internal
841   \since Coin 2.3
842 */
843 SbBool
hasListItemType(const SbName & name,SoType type) const844 SoNodekitCatalog::hasListItemType(const SbName & name, SoType type) const
845 {
846   const SbList <class CatalogItem*> * lptr = &this->items;
847   int part = this->getPartNumber(this->items, name);
848   if (part == SO_CATALOG_NAME_NOT_FOUND) {
849     lptr = &this->delayeditems;
850     part = this->getPartNumber(this->delayeditems, name);
851   }
852   const SbList <class CatalogItem*> & l = *lptr;
853   assert(part >= 0 && part < l.getLength() &&
854          "invalid part number");
855   assert(type != SoType::badType() &&
856          "dont add SoType::badType(), you stupid!");
857   assert(l[part]->islist &&
858          "type must be a list-item type" );
859 
860   return l[part]->itemtypeslist.find( type ) != -1;
861 }
862 
863 #endif // HAVE_NODEKITS
864